diff --git a/doc/ircd.conf.example b/doc/ircd.conf.example index dc97bbd2..90ca6ac0 100755 --- a/doc/ircd.conf.example +++ b/doc/ircd.conf.example @@ -481,6 +481,7 @@ general { min_nonwildcard = 4; min_nonwildcard_simple = 3; max_accept = 100; + max_monitor = 100; anti_nick_flood = yes; max_nick_time = 20 seconds; max_nick_changes = 5; diff --git a/doc/reference.conf b/doc/reference.conf index b98e6b05..78d107ab 100755 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -1004,6 +1004,11 @@ general { /* max accept: maximum allowed /accept's for +g usermode */ max_accept = 20; + /* max monitor: the maximum amount of nicknames a client may have in + * their monitor (server-side notify) list. + */ + max_monitor = 100; + /* nick flood: enable the nickflood control code */ anti_nick_flood = yes; diff --git a/help/opers/monitor b/help/opers/monitor new file mode 100644 index 00000000..646ff769 --- /dev/null +++ b/help/opers/monitor @@ -0,0 +1,27 @@ +MONITOR [nick[,nick]*] + +Manages the online-notification list (similar to WATCH elsewhere). The + must be a single character, one of: + + + adds the given list of nicknames to the monitor list, returns + each given nickname's status as RPL_MONONLINE or RPL_MONOFFLINE + numerics + + - removes the given list of nicknames from the monitor list, does + not return anything + + C clears the monitor list, does not return anything + + L returns the current monitor list as RPL_MONLIST numerics, + terminated with RPL_ENDOFMONLIST + + S returns status of each monitored nickname, as RPL_MONONLINE or + RPL_MONOFFLINE numerics + +For example: + + MONITOR + jilles,kaniini,tomaw + +RPL_MONONLINE numerics return a comma-separated list of nick!user@host +items. RPL_MONOFFLINE and RPL_MONLIST numerics return a comma-separated +list of nicknames. diff --git a/include/monitor.h b/include/monitor.h new file mode 100644 index 00000000..0e711b40 --- /dev/null +++ b/include/monitor.h @@ -0,0 +1,35 @@ +/* + * ircd-ratbox: an advanced Internet Relay Chat Daemon(ircd). + * monitor.h: Code for server-side notify lists. + * + * Copyright (C) 2005 Lee Hardy + * Copyright (C) 2005 ircd-ratbox development team + * + * $Id: monitor.h 6 2005-09-10 01:02:21Z nenolod $ + */ +#ifndef INCLUDED_monitor_h +#define INCLUDED_monitor_h + +struct rb_bh; + +struct monitor +{ + char name[NICKLEN]; + rb_dlink_list users; + rb_dlink_node node; + unsigned int hashv; +}; + +#define MONITOR_HASH_BITS 16 +#define MONITOR_HASH_SIZE (1< + * Copyright (C) 2005 ircd-ratbox development team + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1.Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2.Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3.The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: m_monitor.c 312 2005-11-07 10:47:33Z jilles $ + */ + +#include "stdinc.h" +#include "client.h" +#include "msg.h" +#include "parse.h" +#include "modules.h" +#include "monitor.h" +#include "numeric.h" +#include "s_conf.h" +#include "send.h" + +static int m_monitor(struct Client *, struct Client *, int, const char **); + +struct Message monitor_msgtab = { + "MONITOR", 0, 0, 0, MFLG_SLOW, + {mg_unreg, {m_monitor, 2}, mg_ignore, mg_ignore, mg_ignore, {m_monitor, 2}} +}; + +mapi_clist_av1 monitor_clist[] = { &monitor_msgtab, NULL }; +DECLARE_MODULE_AV1(monitor, NULL, NULL, monitor_clist, NULL, NULL, "$Revision: 312 $"); + +static void +add_monitor(struct Client *client_p, const char *nicks) +{ + char onbuf[BUFSIZE], offbuf[BUFSIZE]; + struct Client *target_p; + struct monitor *monptr; + const char *name; + char *tmp; + char *p; + char *onptr, *offptr; + int mlen, arglen; + int cur_onlen, cur_offlen; + + /* these two are same length, just diff numeric */ + cur_offlen = cur_onlen = mlen = sprintf(onbuf, form_str(RPL_MONONLINE), + me.name, client_p->name, ""); + sprintf(offbuf, form_str(RPL_MONOFFLINE), + me.name, client_p->name, ""); + + onptr = onbuf + mlen; + offptr = offbuf + mlen; + + tmp = LOCAL_COPY(nicks); + + for(name = rb_strtok_r(tmp, ",", &p); name; name = rb_strtok_r(NULL, ",", &p)) + { + if(EmptyString(name) || strlen(name) > NICKLEN-1) + continue; + + if(rb_dlink_list_length(&client_p->localClient->monitor_list) >= + (unsigned long)ConfigFileEntry.max_monitor) + { + char buf[100]; + + if(cur_onlen != mlen) + sendto_one(client_p, "%s", onbuf); + if(cur_offlen != mlen) + sendto_one(client_p, "%s", offbuf); + + if(p) + rb_snprintf(buf, sizeof(buf), "%s,%s", name, p); + else + rb_snprintf(buf, sizeof(buf), "%s", name); + + sendto_one(client_p, form_str(ERR_MONLISTFULL), + me.name, client_p->name, + ConfigFileEntry.max_monitor, buf); + return; + } + + monptr = find_monitor(name, 1); + + /* already monitoring this nick */ + if(rb_dlinkFind(client_p, &monptr->users)) + continue; + + rb_dlinkAddAlloc(client_p, &monptr->users); + rb_dlinkAddAlloc(monptr, &client_p->localClient->monitor_list); + + if((target_p = find_named_person(name)) != NULL) + { + if(cur_onlen + strlen(target_p->name) + + strlen(target_p->username) + strlen(target_p->host) + 3 >= BUFSIZE-3) + { + sendto_one(client_p, "%s", onbuf); + cur_onlen = mlen; + onptr = onbuf + mlen; + } + + if(cur_onlen != mlen) + { + *onptr++ = ','; + cur_onlen++; + } + + arglen = sprintf(onptr, "%s!%s@%s", + target_p->name, target_p->username, + target_p->host); + onptr += arglen; + cur_onlen += arglen; + } + else + { + if(cur_offlen + strlen(name) + 1 >= BUFSIZE-3) + { + sendto_one(client_p, "%s", offbuf); + cur_offlen = mlen; + offptr = offbuf + mlen; + } + + if(cur_offlen != mlen) + { + *offptr++ = ','; + cur_offlen++; + } + + arglen = sprintf(offptr, "%s", name); + offptr += arglen; + cur_offlen += arglen; + } + } + + if(cur_onlen != mlen) + sendto_one(client_p, "%s", onbuf); + if(cur_offlen != mlen) + sendto_one(client_p, "%s", offbuf); +} + +static void +del_monitor(struct Client *client_p, const char *nicks) +{ + struct monitor *monptr; + const char *name; + char *tmp; + char *p; + + if(!rb_dlink_list_length(&client_p->localClient->monitor_list)) + return; + + tmp = LOCAL_COPY(nicks); + + for(name = rb_strtok_r(tmp, ",", &p); name; name = rb_strtok_r(NULL, ",", &p)) + { + if(EmptyString(name)) + continue; + + /* not monitored */ + if((monptr = find_monitor(name, 0)) == NULL) + continue; + + rb_dlinkFindDestroy(client_p, &monptr->users); + rb_dlinkFindDestroy(monptr, &client_p->localClient->monitor_list); + + free_monitor(monptr); + } +} + +static void +list_monitor(struct Client *client_p) +{ + char buf[BUFSIZE]; + struct monitor *monptr; + char *nbuf; + rb_dlink_node *ptr; + int mlen, arglen, cur_len; + + if(!rb_dlink_list_length(&client_p->localClient->monitor_list)) + { + sendto_one(client_p, form_str(RPL_ENDOFMONLIST), + me.name, client_p->name); + return; + } + + cur_len = mlen = sprintf(buf, form_str(RPL_MONLIST), + me.name, client_p->name, ""); + nbuf = buf + mlen; + + RB_DLINK_FOREACH(ptr, client_p->localClient->monitor_list.head) + { + monptr = ptr->data; + + if(cur_len + strlen(monptr->name) + 1 >= BUFSIZE-3) + { + sendto_one(client_p, "%s", buf); + nbuf = buf + mlen; + cur_len = mlen; + } + + if(cur_len != mlen) + { + *nbuf++ = ','; + cur_len++; + } + + arglen = sprintf(nbuf, "%s", monptr->name); + cur_len += arglen; + nbuf += arglen; + } + + sendto_one(client_p, "%s", buf); + sendto_one(client_p, form_str(RPL_ENDOFMONLIST), + me.name, client_p->name); +} + +static void +show_monitor_status(struct Client *client_p) +{ + char onbuf[BUFSIZE], offbuf[BUFSIZE]; + struct Client *target_p; + struct monitor *monptr; + char *onptr, *offptr; + int cur_onlen, cur_offlen; + int mlen, arglen; + rb_dlink_node *ptr; + + mlen = cur_onlen = sprintf(onbuf, form_str(RPL_MONONLINE), + me.name, client_p->name, ""); + cur_offlen = sprintf(offbuf, form_str(RPL_MONOFFLINE), + me.name, client_p->name, ""); + + onptr = onbuf + mlen; + offptr = offbuf + mlen; + + RB_DLINK_FOREACH(ptr, client_p->localClient->monitor_list.head) + { + monptr = ptr->data; + + if((target_p = find_named_person(monptr->name)) != NULL) + { + if(cur_onlen + strlen(target_p->name) + + strlen(target_p->username) + strlen(target_p->host) + 3 >= BUFSIZE-3) + { + sendto_one(client_p, "%s", onbuf); + cur_onlen = mlen; + onptr = onbuf + mlen; + } + + if(cur_onlen != mlen) + { + *onptr++ = ','; + cur_onlen++; + } + + arglen = sprintf(onptr, "%s!%s@%s", + target_p->name, target_p->username, + target_p->host); + onptr += arglen; + cur_onlen += arglen; + } + else + { + if(cur_offlen + strlen(monptr->name) + 1 >= BUFSIZE-3) + { + sendto_one(client_p, "%s", offbuf); + cur_offlen = mlen; + offptr = offbuf + mlen; + } + + if(cur_offlen != mlen) + { + *offptr++ = ','; + cur_offlen++; + } + + arglen = sprintf(offptr, "%s", monptr->name); + offptr += arglen; + cur_offlen += arglen; + } + } + + if(cur_onlen != mlen) + sendto_one(client_p, "%s", onbuf); + if(cur_offlen != mlen) + sendto_one(client_p, "%s", offbuf); +} + +static int +m_monitor(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +{ + switch(parv[1][0]) + { + case '+': + if(parc < 3 || EmptyString(parv[2])) + { + sendto_one(client_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "MONITOR"); + return 0; + } + + add_monitor(source_p, parv[2]); + break; + case '-': + if(parc < 3 || EmptyString(parv[2])) + { + sendto_one(client_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "MONITOR"); + return 0; + } + + del_monitor(source_p, parv[2]); + break; + + case 'C': + case 'c': + clear_monitor(source_p); + break; + + case 'L': + case 'l': + list_monitor(source_p); + break; + + case 'S': + case 's': + show_monitor_status(source_p); + break; + + default: + break; + } + + return 0; +} + diff --git a/src/monitor.c b/src/monitor.c new file mode 100644 index 00000000..6444ed1f --- /dev/null +++ b/src/monitor.c @@ -0,0 +1,154 @@ +/* + * ircd-ratbox: an advanced Internet Relay Chat Daemon(ircd). + * monitor.c - Code for server-side notify lists + * + * Copyright (C) 2005 Lee Hardy + * Copyright (C) 2005 ircd-ratbox development team + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1.Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2.Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3.The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $Id: monitor.c 3520 2007-06-30 22:15:35Z jilles $ + */ +#include "stdinc.h" +#include "client.h" +#include "monitor.h" +#include "hash.h" +#include "numeric.h" +#include "send.h" + +static rb_dlink_list monitorTable[MONITOR_HASH_SIZE]; +static rb_bh *monitor_heap; + +void +init_monitor(void) +{ + monitor_heap = rb_bh_create(sizeof(struct monitor), MONITOR_HEAP_SIZE, "monitor_heap"); +} + +static inline unsigned int +hash_monitor_nick(const char *name) +{ + return fnv_hash_upper((const unsigned char *)name, MONITOR_HASH_BITS); +} + +struct monitor * +find_monitor(const char *name, int add) +{ + struct monitor *monptr; + rb_dlink_node *ptr; + + unsigned int hashv = hash_monitor_nick(name); + + RB_DLINK_FOREACH(ptr, monitorTable[hashv].head) + { + monptr = ptr->data; + if(!irccmp(monptr->name, name)) + return monptr; + } + + if(add) + { + monptr = rb_bh_alloc(monitor_heap); + rb_strlcpy(monptr->name, name, sizeof(monptr->name)); + monptr->hashv = hashv; + + rb_dlinkAdd(monptr, &monptr->node, &monitorTable[hashv]); + return monptr; + } + + return NULL; +} + +void +free_monitor(struct monitor *monptr) +{ + if (rb_dlink_list_length(&monptr->users) > 0) + return; + + rb_dlinkDelete(&monptr->node, &monitorTable[monptr->hashv]); + rb_bh_free(monitor_heap, monptr); +} + +/* monitor_signon() + * + * inputs - client who has just connected + * outputs - + * side effects - notifies any clients monitoring this nickname that it has + * connected to the network + */ +void +monitor_signon(struct Client *client_p) +{ + char buf[USERHOST_REPLYLEN]; + struct monitor *monptr = find_monitor(client_p->name, 0); + + /* noones watching this nick */ + if(monptr == NULL) + return; + + rb_snprintf(buf, sizeof(buf), "%s!%s@%s", client_p->name, client_p->username, client_p->host); + + sendto_monitor(monptr, form_str(RPL_MONONLINE), me.name, "*", buf); +} + +/* monitor_signoff() + * + * inputs - client who is exiting + * outputs - + * side effects - notifies any clients monitoring this nickname that it has + * left the network + */ +void +monitor_signoff(struct Client *client_p) +{ + struct monitor *monptr = find_monitor(client_p->name, 0); + + /* noones watching this nick */ + if(monptr == NULL) + return; + + sendto_monitor(monptr, form_str(RPL_MONOFFLINE), me.name, "*", + client_p->name); +} + +void +clear_monitor(struct Client *client_p) +{ + struct monitor *monptr; + rb_dlink_node *ptr, *next_ptr; + + RB_DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->localClient->monitor_list.head) + { + monptr = ptr->data; + + rb_dlinkFindDestroy(client_p, &monptr->users); + rb_free_rb_dlink_node(ptr); + + free_monitor(ptr->data); + } + + client_p->localClient->monitor_list.head = client_p->localClient->monitor_list.tail = NULL; + client_p->localClient->monitor_list.length = 0; +} diff --git a/src/supported.c b/src/supported.c index 503e47f7..efcaf7f2 100644 --- a/src/supported.c +++ b/src/supported.c @@ -281,7 +281,7 @@ isupport_targmax(const void *ptr) { static char result[200]; - rb_snprintf(result, sizeof result, "NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:%d,NOTICE:%d,ACCEPT:", + rb_snprintf(result, sizeof result, "NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:%d,NOTICE:%d,ACCEPT:,MONITOR:", ConfigFileEntry.max_targets, ConfigFileEntry.max_targets); return result; @@ -338,6 +338,7 @@ init_isupport(void) add_isupport("CPRIVMSG", isupport_string, ""); add_isupport("CNOTICE", isupport_string, ""); add_isupport("DEAF", isupport_umode, "D"); + add_isupport("MONITOR", isupport_intptr, &ConfigFileEntry.max_monitor); add_isupport("FNC", isupport_string, ""); add_isupport("TARGMAX", isupport_targmax, NULL); add_isupport("EXTBAN", isupport_extban, NULL);