From 717238d2a2864c51871a3f579f3d40f032c20f46 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker <jilles@stack.nl> Date: Sun, 29 Aug 2010 01:26:00 +0200 Subject: [PATCH] Add target change for channels. This has a separate enabling option channel::channel_target_change. It applies to PRIVMSG, NOTICE and TOPIC by unvoiced unopped non-opers. The same slots are used for channels and users. --- doc/example.conf | 1 + doc/reference.conf | 6 ++++++ doc/tgchange.txt | 29 ++++++++++++++++------------- include/s_conf.h | 1 + include/tgchange.h | 2 ++ modules/core/m_message.c | 15 +++++++++++++++ modules/m_topic.c | 10 ++++++++++ src/newconf.c | 1 + src/s_conf.c | 1 + src/tgchange.c | 22 ++++++++++++++++++++-- 10 files changed, 73 insertions(+), 15 deletions(-) diff --git a/doc/example.conf b/doc/example.conf index e1fbf2d3..559388d6 100755 --- a/doc/example.conf +++ b/doc/example.conf @@ -337,6 +337,7 @@ channel { kick_on_split_riding = no; only_ascii_channels = no; resv_forcepart = yes; + channel_target_change = yes; }; serverhide { diff --git a/doc/reference.conf b/doc/reference.conf index 03fc0412..c7e72bd2 100755 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -755,6 +755,12 @@ channel { * when a RESV is issued. */ resv_forcepart = yes; + + /* channel target change: restrict how many channels users can + * message per unit of time. IRC operators, channel operators and + * voiced users are exempt. + */ + channel_target_change = yes; }; diff --git a/doc/tgchange.txt b/doc/tgchange.txt index 483ca23c..faea0090 100644 --- a/doc/tgchange.txt +++ b/doc/tgchange.txt @@ -3,15 +3,17 @@ Lee H <lee -at- leeh.co.uk> --------------------------- Reworked by Jilles Tjoelker, February 2010. +Channel target change added by Jilles Tjoelker, August 2010. If the server you are using uses the target change mechanism, then -restrictions are placed on how many different users you can message in a set -timeframe. This also applies to invites. +restrictions are placed on how many different users and/or channels you can +message in a set timeframe. This also applies to invites (for the target +user) and topic changes. -Target change does not apply to channels, ctcp replies, messages to -yourself or messages to services. +Target change does not apply to ctcp replies, messages to yourself, messages +to services and joins. -You will have a set number of 'slots', each different client you message +You will have a set number of 'slots', each different target you message will take up one slot. A client doing a nick change will not use a new slot, however a client disconnecting from the server it is on and reconnecting will. You will receive 1 new slot roughly every minute. @@ -20,14 +22,14 @@ Additionally, clients that message or invite you are placed in one of a small number of special slots, in many cases allowing replies without using a slot. -When all slots are filled, messages to new clients will not be accepted. -Messages to clients already filling a slot will be accepted. If all slots +When all slots are filled, messages to new targets will not be accepted. +Messages to targets already filling a slot will be accepted. If all slots are full, you will receive the ERR_TARGCHANGE numeric, number 707 in the form: -:<server> 707 <yournick> <targetnick> :Targets changing too fast, message dropped +:<server> 707 <yournick> <target> :Targets changing too fast, message dropped -The slots are operated in an LRU (least recently used), so the person you -have talked to least recently will be replaced. +The slots are operated in an LRU (least recently used), so the person or +channel you have talked to least recently will be replaced. The number of slots in use will be kept through a reconnection, though the information in those slots will be dropped. However, you will always @@ -35,9 +37,10 @@ receive one free slot on a reconnection. Other servers using this mechanism will also be made aware of details about slots. Target change does not apply if you are opped or voiced in a channel, and -you are messaging a client within that channel. This can be done explicitly -using the CNOTICE and CPRIVMSG commands, see /quote help cnotice and /quote -help cprivmsg, but is also implicit in a normal /msg, /notice or /invite. +you are messaging that channel or a client within that channel. The latter +can be done explicitly using the CNOTICE and CPRIVMSG commands, see +/quote help cnotice and /quote help cprivmsg, but is also implicit in a +normal /msg, /notice or /invite. -- $Id: tgchange.txt 6 2005-09-10 01:02:21Z nenolod $ diff --git a/include/s_conf.h b/include/s_conf.h index 03e0d5fa..b3e60951 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -246,6 +246,7 @@ struct config_channel_entry int kick_on_split_riding; int only_ascii_channels; int resv_forcepart; + int channel_target_change; }; struct config_server_hide diff --git a/include/tgchange.h b/include/tgchange.h index e3e4fcce..b2cc684c 100644 --- a/include/tgchange.h +++ b/include/tgchange.h @@ -30,6 +30,8 @@ struct Channel *find_allowing_channel(struct Client *source_p, struct Client *target_p); /* checks if source_p is allowed to send to target_p */ int add_target(struct Client *source_p, struct Client *target_p); +/* checks if source_p is allowed to send to chptr */ +int add_channel_target(struct Client *source_p, struct Channel *chptr); /* allows source_p to send to target_p */ void add_reply_target(struct Client *source_p, struct Client *target_p); diff --git a/modules/core/m_message.c b/modules/core/m_message.c index 636bb8dc..cb4d51d1 100644 --- a/modules/core/m_message.c +++ b/modules/core/m_message.c @@ -510,6 +510,14 @@ msg_channel(int p_or_n, const char *command, /* chanops and voiced can flood their own channel with impunity */ if((result = can_send(chptr, source_p, NULL))) { + if(result != CAN_SEND_OPV && MyClient(source_p) && + !IsOper(source_p) && + !add_channel_target(source_p, chptr)) + { + sendto_one(source_p, form_str(ERR_TARGCHANGE), + me.name, source_p->name, chptr->chname); + return; + } if(result == CAN_SEND_OPV || !flood_attack_channel(p_or_n, source_p, chptr, chptr->chname)) { @@ -533,6 +541,13 @@ msg_channel(int p_or_n, const char *command, (!(chptr->mode.mode & MODE_NOPRIVMSGS) || IsMember(source_p, chptr))) { + if(MyClient(source_p) && !IsOper(source_p) && + !add_channel_target(source_p, chptr)) + { + sendto_one(source_p, form_str(ERR_TARGCHANGE), + me.name, source_p->name, chptr->chname); + return; + } if(!flood_attack_channel(p_or_n, source_p, chptr, chptr->chname)) { sendto_channel_opmod(client_p, source_p, chptr, diff --git a/modules/m_topic.c b/modules/m_topic.c index 55be75ae..cf7b2853 100644 --- a/modules/m_topic.c +++ b/modules/m_topic.c @@ -39,6 +39,7 @@ #include "parse.h" #include "modules.h" #include "packet.h" +#include "tgchange.h" static int m_topic(struct Client *, struct Client *, int, const char **); static int ms_topic(struct Client *, struct Client *, int, const char **); @@ -114,6 +115,15 @@ m_topic(struct Client *client_p, struct Client *source_p, int parc, const char * return 0; } + if(MyClient(source_p) && !is_chanop_voiced(msptr) && + !IsOper(source_p) && + !add_channel_target(source_p, chptr)) + { + sendto_one(source_p, form_str(ERR_TARGCHANGE), + me.name, source_p->name, chptr->chname); + return 0; + } + if(((chptr->mode.mode & MODE_TOPICLIMIT) == 0 || is_chanop(msptr)) && (!MyClient(source_p) || diff --git a/src/newconf.c b/src/newconf.c index 1769d35d..6e4fca20 100644 --- a/src/newconf.c +++ b/src/newconf.c @@ -2199,6 +2199,7 @@ static struct ConfEntry conf_channel_table[] = { "use_knock", CF_YESNO, NULL, 0, &ConfigChannel.use_knock }, { "use_forward", CF_YESNO, NULL, 0, &ConfigChannel.use_forward }, { "resv_forcepart", CF_YESNO, NULL, 0, &ConfigChannel.resv_forcepart }, + { "channel_target_change", CF_YESNO, NULL, 0, &ConfigChannel.channel_target_change }, { "\0", 0, NULL, 0, NULL } }; diff --git a/src/s_conf.c b/src/s_conf.c index 48192c9e..f75472b0 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -772,6 +772,7 @@ set_default_conf(void) ConfigChannel.no_join_on_split = NO; ConfigChannel.no_create_on_split = YES; ConfigChannel.resv_forcepart = YES; + ConfigChannel.channel_target_change = YES; ConfigServerHide.flatten_links = 0; ConfigServerHide.links_delay = 300; diff --git a/src/tgchange.c b/src/tgchange.c index 387b1917..d06ad41f 100644 --- a/src/tgchange.c +++ b/src/tgchange.c @@ -30,6 +30,8 @@ #include "hash.h" #include "s_newconf.h" +static int add_hashed_target(struct Client *source_p, uint32_t hashv); + struct Channel * find_allowing_channel(struct Client *source_p, struct Client *target_p) { @@ -48,9 +50,7 @@ find_allowing_channel(struct Client *source_p, struct Client *target_p) int add_target(struct Client *source_p, struct Client *target_p) { - int i, j; uint32_t hashv; - uint32_t *targets; /* can msg themselves or services without using any target slots */ if(source_p == target_p || IsService(target_p)) @@ -65,6 +65,24 @@ add_target(struct Client *source_p, struct Client *target_p) return 1; hashv = fnv_hash_upper((const unsigned char *)use_id(target_p), 32); + return add_hashed_target(source_p, hashv); +} + +int +add_channel_target(struct Client *source_p, struct Channel *chptr) +{ + uint32_t hashv; + + hashv = fnv_hash_upper((const unsigned char *)chptr->chname, 32); + return add_hashed_target(source_p, hashv); +} + +static int +add_hashed_target(struct Client *source_p, uint32_t hashv) +{ + int i, j; + uint32_t *targets; + targets = source_p->localClient->targets; /* check for existing target, and move it to the head */