From 9e94d9ea13fcf649e4fc3fe8fbcad249cc5a0ce6 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Sat, 28 Aug 2010 20:02:42 -0400 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 2bc25de..33050fa 100755 --- a/doc/example.conf +++ b/doc/example.conf @@ -400,6 +400,7 @@ channel { cycle_host_change = yes; host_in_topic = yes; resv_forcepart = yes; + channel_target_change = yes; }; serverhide { diff --git a/doc/reference.conf b/doc/reference.conf index 6d6f2ce..044b709 100755 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -914,6 +914,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 6ebe504..8c5c530 100644 --- a/doc/tgchange.txt +++ b/doc/tgchange.txt @@ -3,15 +3,17 @@ Lee H --------------------------- 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: -: 707 :Targets changing too fast, message dropped +: 707 :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,8 +37,9 @@ 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. -- diff --git a/include/s_conf.h b/include/s_conf.h index d8a7fcb..a54da0c 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -261,6 +261,7 @@ struct config_channel_entry int cycle_host_change; int host_in_topic; int resv_forcepart; + int channel_target_change; int exempt_cmode_c; int exempt_cmode_C; diff --git a/include/tgchange.h b/include/tgchange.h index e3e4fcc..b2cc684 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 ff2442d..e5505c2 100644 --- a/modules/core/m_message.c +++ b/modules/core/m_message.c @@ -536,6 +536,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)) { @@ -586,6 +594,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 3e8b4bf..2fd2072 100644 --- a/modules/m_topic.c +++ b/modules/m_topic.c @@ -38,6 +38,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 **); @@ -115,6 +116,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(MyClient(source_p) && (chptr->mode.mode & MODE_TOPICLIMIT) && !is_any_op(msptr)) { if(IsOverride(source_p)) diff --git a/src/newconf.c b/src/newconf.c index 6071fa9..6667109 100644 --- a/src/newconf.c +++ b/src/newconf.c @@ -2287,6 +2287,7 @@ static struct ConfEntry conf_channel_table[] = { "use_knock", CF_YESNO, NULL, 0, &ConfigChannel.use_knock }, { "use_local_channels", CF_YESNO, NULL, 0, &ConfigChannel.use_local_channels }, { "resv_forcepart", CF_YESNO, NULL, 0, &ConfigChannel.resv_forcepart }, + { "channel_target_change", CF_YESNO, NULL, 0, &ConfigChannel.channel_target_change }, { "exempt_cmode_c", CF_YESNO, NULL, 0, &ConfigChannel.exempt_cmode_c }, { "exempt_cmode_C", CF_YESNO, NULL, 0, &ConfigChannel.exempt_cmode_C }, { "exempt_cmode_D", CF_YESNO, NULL, 0, &ConfigChannel.exempt_cmode_D }, diff --git a/src/s_conf.c b/src/s_conf.c index 045c3c2..5147167 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -787,6 +787,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; ConfigChannel.exempt_cmode_c = NO; ConfigChannel.exempt_cmode_C = NO; diff --git a/src/tgchange.c b/src/tgchange.c index 387b191..d06ad41 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 */