unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

floodprot.c (59896B)

      1 /*
      2  * Channel Mode +f and +F
      3  * (C) Copyright 2019-.. Syzop and the UnrealIRCd team
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 1, or (at your option)
      8  * any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program; if not, write to the Free Software
     17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 #include "unrealircd.h"
     21 
     22 ModuleHeader MOD_HEADER
     23   = {
     24 	"chanmodes/floodprot",
     25 	"6.0",
     26 	"Channel Mode +f and +F",
     27 	"UnrealIRCd Team",
     28 	"unrealircd-6",
     29     };
     30 
     31 typedef enum Flood {
     32 	CHFLD_CTCP	= 0,
     33 	CHFLD_JOIN	= 1,
     34 	CHFLD_KNOCK	= 2,
     35 	CHFLD_MSG	= 3,
     36 	CHFLD_NICK	= 4,
     37 	CHFLD_TEXT	= 5,
     38 	CHFLD_REPEAT	= 6,
     39 } Flood;
     40 #define NUMFLD	7 /* 7 flood types */
     41 
     42 /** Configuration settings */
     43 struct {
     44 	unsigned char modef_default_unsettime;
     45 	unsigned char modef_max_unsettime;
     46 	long boot_delay;
     47 	long split_delay;
     48 	int modef_alternate_action_percentage_threshold;
     49 	unsigned char modef_alternative_ban_action_unsettime;
     50 	char *default_profile;
     51 } cfg;
     52 
     53 typedef struct FloodType {
     54 	char letter;
     55 	Flood index;
     56 	char *description;
     57 	char default_action;
     58 	char *actions;
     59 	char *alternative_ban_action;
     60 	int timedban_required;
     61 } FloodType;
     62 
     63 /* All the floodtypes that are tracked.
     64  * IMPORTANT: the first row MUST be in alphabetic order!!
     65  */
     66 FloodType floodtypes[] = {
     67 	{ 'c', CHFLD_CTCP,	"CTCPflood",		'C',	"",	NULL,						0, },
     68 	{ 'j', CHFLD_JOIN,	"joinflood",		'i',	"R",	"~security-group:unknown-users",		0, },
     69 	{ 'k', CHFLD_KNOCK,	"knockflood",		'K',	"",	NULL,						0, },
     70 	{ 'm', CHFLD_MSG,	"msg/noticeflood",	'm',	"M",	"~quiet:~security-group:unknown-users",		0, },
     71 	{ 'n', CHFLD_NICK,	"nickflood",		'N',	"",	"~nickchange:~security-group:unknown-users",	0, },
     72 	{ 't', CHFLD_TEXT,	"msg/noticeflood",	'\0',	"bd",	NULL,						1, },
     73 	{ 'r', CHFLD_REPEAT,	"repeating",		'\0',	"bd",	NULL,						1, },
     74 };
     75 
     76 #define MODEF_DEFAULT_UNSETTIME		cfg.modef_default_unsettime
     77 #define MODEF_MAX_UNSETTIME		cfg.modef_max_unsettime
     78 
     79 typedef struct ChannelFloodProtection ChannelFloodProtection;
     80 typedef struct ChannelFloodProfile ChannelFloodProfile;
     81 typedef struct RemoveChannelModeTimer RemoveChannelModeTimer;
     82 
     83 struct RemoveChannelModeTimer {
     84 	struct RemoveChannelModeTimer *prev, *next;
     85 	Channel *channel;
     86 	char m; /* mode to be removed */
     87 	time_t when; /* scheduled at */
     88 };
     89 
     90 typedef struct MemberFlood MemberFlood;
     91 struct MemberFlood {
     92 	unsigned short nmsg;
     93 	unsigned short nmsg_repeat;
     94 	time_t firstmsg;
     95 	uint64_t lastmsg;
     96 	uint64_t prevmsg;
     97 };
     98 
     99 /* Maximum timers, iotw: max number of possible actions.
    100  * Currently this is: CNmMKiRd (8)
    101  * But bumped to 15 because we now have cmode.flood_type_action
    102  * so there could be more ;).
    103  */
    104 #define MAXCHMODEFACTIONS 15
    105 
    106 /** Per-channel flood protection settings and counters */
    107 struct ChannelFloodProtection {
    108 	unsigned short	per; /**< setting: per <XX> seconds */
    109 	time_t		timer[NUMFLD]; /**< runtime: timers */
    110 	unsigned short	counter[NUMFLD]; /**< runtime: counters */
    111 	unsigned short	counter_unknown_users[NUMFLD]; /**< runtime: counters */
    112 	unsigned short	limit[NUMFLD]; /**< setting: limit */
    113 	unsigned char	action[NUMFLD]; /**< setting: action */
    114 	unsigned char	remove_after[NUMFLD]; /**< setting: remove-after <this> minutes */
    115 	unsigned char   timers_running[MAXCHMODEFACTIONS+1]; /**< if for example a '-m' timer is running then this contains 'm' */
    116 	char *profile;
    117 };
    118 
    119 struct ChannelFloodProfile {
    120 	ChannelFloodProfile *prev, *next;
    121 	ChannelFloodProtection settings;
    122 };
    123 
    124 /* Global variables */
    125 ModDataInfo *mdflood = NULL;
    126 Cmode_t EXTMODE_FLOODLIMIT = 0L;
    127 Cmode_t EXTMODE_FLOOD_PROFILE = 0L;
    128 static int timedban_available = 1; /**< Set to 1 if extbans/timedban module is loaded. Assumed 1 during config load due to set::modes-on-join race. */
    129 RemoveChannelModeTimer *removechannelmodetimer_list = NULL;
    130 ChannelFloodProfile *channel_flood_profiles = NULL;
    131 char *floodprot_msghash_key = NULL;
    132 long long floodprot_splittime = 0;
    133 
    134 #define IsFloodLimit(x)	(((x)->mode.mode & EXTMODE_FLOODLIMIT) || ((x)->mode.mode & EXTMODE_FLOOD_PROFILE) || (cfg.default_profile && GETPARASTRUCT(channel, 'F')))
    135 
    136 /* Forward declarations */
    137 static void init_config(void);
    138 int floodprot_rehash_complete(void);
    139 int floodprot_config_test_set_block(ConfigFile *, ConfigEntry *, int, int *);
    140 int floodprot_config_run_set_block(ConfigFile *, ConfigEntry *, int);
    141 int floodprot_config_test_antiflood_block(ConfigFile *, ConfigEntry *, int, int *);
    142 int floodprot_config_run_antiflood_block(ConfigFile *, ConfigEntry *, int);
    143 void floodprottimer_del(Channel *channel, ChannelFloodProtection *fld, char mflag);
    144 void floodprottimer_stopchantimers(Channel *channel);
    145 static inline char *chmodefstrhelper(char *buf, char t, char tdef, unsigned short l, unsigned char a, unsigned char r);
    146 static int compare_floodprot_modes(ChannelFloodProtection *a, ChannelFloodProtection *b);
    147 static int do_floodprot(Channel *channel, Client *client, int what);
    148 char *channel_modef_string(ChannelFloodProtection *x, char *str);
    149 void do_floodprot_action(Channel *channel, int what);
    150 void floodprottimer_add(Channel *channel, ChannelFloodProtection *fld, char mflag, time_t when);
    151 uint64_t gen_floodprot_msghash(const char *text);
    152 int cmodef_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what);
    153 void *cmodef_put_param(void *r_in, const char *param);
    154 const char *cmodef_get_param(void *r_in);
    155 const char *cmodef_conv_param(const char *param_in, Client *client, Channel *channel);
    156 int cmodef_free_param(void *r, int soft);
    157 void *cmodef_dup_struct(void *r_in);
    158 int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx);
    159 int cmodef_profile_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what);
    160 void *cmodef_profile_put_param(void *r_in, const char *param);
    161 const char *cmodef_profile_get_param(void *r_in);
    162 const char *cmodef_profile_conv_param(const char *param_in, Client *client, Channel *channel);
    163 int cmodef_profile_sjoin_check(Channel *channel, void *ourx, void *theirx);
    164 int floodprot_join(Client *client, Channel *channel, MessageTag *mtags);
    165 EVENT(modef_event);
    166 int cmodef_channel_create(Channel *channel);
    167 int cmodef_channel_destroy(Channel *channel, int *should_destroy);
    168 int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype);
    169 int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype);
    170 int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment);
    171 int floodprot_nickchange(Client *client, MessageTag *mtags, const char *oldnick);
    172 int floodprot_chanmode_del(Channel *channel, int m);
    173 void memberflood_free(ModData *md);
    174 int floodprot_stats(Client *client, const char *flag);
    175 void floodprot_free_removechannelmodetimer_list(ModData *m);
    176 void floodprot_free_msghash_key(ModData *m);
    177 CMD_OVERRIDE_FUNC(floodprot_override_mode);
    178 ChannelFloodProtection *get_channel_flood_profile(const char *name);
    179 int parse_channel_mode_flood(const char *param, ChannelFloodProtection *fld, int strict, Client *client, const char **error_out);
    180 int parse_channel_mode_flood_failed(const char **error_out, ChannelFloodProtection *fld, FORMAT_STRING(const char *fmt), ...) __attribute__((format(printf,3,4)));
    181 int floodprot_server_quit(Client *client, MessageTag *mtags);
    182 void inherit_settings(ChannelFloodProtection *from, ChannelFloodProtection *to);
    183 void reapply_profiles(void);
    184 
    185 MOD_TEST()
    186 {
    187 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, floodprot_config_test_set_block);
    188 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, floodprot_config_test_antiflood_block);
    189 	return MOD_SUCCESS;
    190 }
    191 
    192 MOD_INIT()
    193 {
    194 	CmodeInfo creq;
    195 	ModDataInfo mreq;
    196 
    197 	MARK_AS_OFFICIAL_MODULE(modinfo);
    198 
    199 	LoadPersistentLongLong(modinfo, floodprot_splittime);
    200 
    201 	memset(&creq, 0, sizeof(creq));
    202 	creq.paracount = 1;
    203 	creq.is_ok = cmodef_is_ok;
    204 	creq.letter = 'f';
    205 	creq.unset_with_param = 1; /* ah yeah, +f is special! */
    206 	creq.put_param = cmodef_put_param;
    207 	creq.get_param = cmodef_get_param;
    208 	creq.conv_param = cmodef_conv_param;
    209 	creq.free_param = cmodef_free_param;
    210 	creq.dup_struct = cmodef_dup_struct;
    211 	creq.sjoin_check = cmodef_sjoin_check;
    212 	CmodeAdd(modinfo->handle, creq, &EXTMODE_FLOODLIMIT);
    213 
    214 	memset(&creq, 0, sizeof(creq));
    215 	creq.paracount = 1;
    216 	creq.is_ok = cmodef_profile_is_ok;
    217 	creq.letter = 'F';
    218 	creq.put_param = cmodef_profile_put_param;
    219 	creq.get_param = cmodef_profile_get_param;
    220 	creq.conv_param = cmodef_profile_conv_param;
    221 	creq.free_param = cmodef_free_param; // +f & +F uses same code
    222 	creq.dup_struct = cmodef_dup_struct; // +f & +F uses same code
    223 	creq.sjoin_check = cmodef_profile_sjoin_check;
    224 	CmodeAdd(modinfo->handle, creq, &EXTMODE_FLOOD_PROFILE);
    225 
    226 	init_config();
    227 
    228 	LoadPersistentPointer(modinfo, removechannelmodetimer_list, floodprot_free_removechannelmodetimer_list);
    229 	LoadPersistentPointer(modinfo, floodprot_msghash_key, floodprot_free_msghash_key);
    230 
    231 	memset(&mreq, 0, sizeof(mreq));
    232 	mreq.name = "floodprot";
    233 	mreq.type = MODDATATYPE_MEMBERSHIP;
    234 	mreq.free = memberflood_free;
    235 	mdflood = ModDataAdd(modinfo->handle, mreq);
    236 	if (!mdflood)
    237 	        abort();
    238 	if (!floodprot_msghash_key)
    239 	{
    240 		floodprot_msghash_key = safe_alloc(16);
    241 		siphash_generate_key(floodprot_msghash_key);
    242 	}
    243 
    244 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, floodprot_config_run_set_block);
    245 	HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, floodprot_config_run_antiflood_block);
    246 	HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, floodprot_can_send_to_channel);
    247 	HookAdd(modinfo->handle, HOOKTYPE_CHANMSG, 0, floodprot_post_chanmsg);
    248 	HookAdd(modinfo->handle, HOOKTYPE_KNOCK, 0, floodprot_knock);
    249 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_NICKCHANGE, 0, floodprot_nickchange);
    250 	HookAdd(modinfo->handle, HOOKTYPE_REMOTE_NICKCHANGE, 0, floodprot_nickchange);
    251 	HookAdd(modinfo->handle, HOOKTYPE_MODECHAR_DEL, 0, floodprot_chanmode_del);
    252 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, floodprot_join);
    253 	HookAdd(modinfo->handle, HOOKTYPE_REMOTE_JOIN, 0, floodprot_join);
    254 	HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_CREATE, 0, cmodef_channel_create);
    255 	HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_DESTROY, 0, cmodef_channel_destroy);
    256 	HookAdd(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, 0, floodprot_rehash_complete);
    257 	HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, floodprot_stats);
    258 	HookAdd(modinfo->handle, HOOKTYPE_SERVER_QUIT, 0, floodprot_server_quit);
    259 	return MOD_SUCCESS;
    260 }
    261 
    262 MOD_LOAD()
    263 {
    264 	EventAdd(modinfo->handle, "modef_event", modef_event, NULL, 10000, 0);
    265 	CommandOverrideAdd(modinfo->handle, "MODE", 0, floodprot_override_mode);
    266 	floodprot_rehash_complete();
    267 	reapply_profiles();
    268 	return MOD_SUCCESS;
    269 }
    270 
    271 void free_channel_flood_profile(ChannelFloodProfile *f)
    272 {
    273 	safe_free(f->settings.profile);
    274 	safe_free(f);
    275 }
    276 
    277 void free_channel_flood_profiles(void)
    278 {
    279 	ChannelFloodProfile *f, *f_next;
    280 
    281 	for (f = channel_flood_profiles; f; f = f_next)
    282 	{
    283 		f_next = f->next;
    284 		DelListItem(f, channel_flood_profiles);
    285 		free_channel_flood_profile(f);
    286 	}
    287 }
    288 
    289 MOD_UNLOAD()
    290 {
    291 	SavePersistentPointer(modinfo, removechannelmodetimer_list);
    292 	SavePersistentPointer(modinfo, floodprot_msghash_key);
    293 	SavePersistentLongLong(modinfo, floodprot_splittime);
    294 
    295 	free_channel_flood_profiles();
    296 
    297 	return MOD_SUCCESS;
    298 }
    299 
    300 int floodprot_rehash_complete(void)
    301 {
    302 	timedban_available = is_module_loaded("extbans/timedban");
    303 	return 0;
    304 }
    305 
    306 /** Set a new channel anti flood profile.
    307  * Caller MUST ensure that the 'value' is valid, eg by calling
    308  * parse_channel_mode_flood() or is_ok() prior.
    309  */
    310 static void set_channel_flood_profile(const char *name, const char *value)
    311 {
    312 	ChannelFloodProfile *f;
    313 
    314 	for (f = channel_flood_profiles; f; f = f->next)
    315 		if (!strcasecmp(f->settings.profile, name))
    316 			break;
    317 	if (!f)
    318 	{
    319 		f = safe_alloc(sizeof(ChannelFloodProfile));
    320 		AddListItem(f, channel_flood_profiles);
    321 	}
    322 
    323 	safe_strdup(f->settings.profile, name);
    324 	cmodef_put_param(&f->settings, value);
    325 }
    326 
    327 static void init_default_channel_flood_profiles(void)
    328 {
    329 	ChannelFloodProfile *f;
    330 
    331 	f = safe_alloc(sizeof(ChannelFloodProfile));
    332 	cmodef_put_param(&f->settings, "[10j#R10,30m#M10,7c#C15,5n#N15,10k#K15]:15");
    333 	safe_strdup(f->settings.profile, "very-strict");
    334 	AddListItem(f, channel_flood_profiles);
    335 
    336 	f = safe_alloc(sizeof(ChannelFloodProfile));
    337 	cmodef_put_param(&f->settings, "[15j#R10,40m#M10,7c#C15,8n#N15,10k#K15]:15");
    338 	safe_strdup(f->settings.profile, "strict");
    339 	AddListItem(f, channel_flood_profiles);
    340 
    341 	f = safe_alloc(sizeof(ChannelFloodProfile));
    342 	cmodef_put_param(&f->settings, "[30j#R10,40m#M10,7c#C15,8n#N15,10k#K15]:15");
    343 	safe_strdup(f->settings.profile, "normal");
    344 	AddListItem(f, channel_flood_profiles);
    345 
    346 	f = safe_alloc(sizeof(ChannelFloodProfile));
    347 	cmodef_put_param(&f->settings, "[45j#R10,60m#M10,7c#C15,10n#N15,10k#K15]:15");
    348 	safe_strdup(f->settings.profile, "relaxed");
    349 	AddListItem(f, channel_flood_profiles);
    350 
    351 	f = safe_alloc(sizeof(ChannelFloodProfile));
    352 	cmodef_put_param(&f->settings, "[60j#R10,90m#M10,7c#C15,10n#N15,10k#K15]:15");
    353 	safe_strdup(f->settings.profile, "very-relaxed");
    354 	AddListItem(f, channel_flood_profiles);
    355 
    356 	f = safe_alloc(sizeof(ChannelFloodProfile));
    357 	safe_strdup(f->settings.profile, "off");
    358 	AddListItem(f, channel_flood_profiles);
    359 }
    360 
    361 static void init_config(void)
    362 {
    363 	/* This sets some default values */
    364 	memset(&cfg, 0, sizeof(cfg));
    365 	cfg.modef_default_unsettime = 0;
    366 	cfg.modef_max_unsettime = 60; /* 1 hour seems enough :p */
    367 	cfg.boot_delay = 75;
    368 	cfg.split_delay = 75;
    369 	cfg.modef_alternate_action_percentage_threshold = 75; /* 75% */
    370 	cfg.modef_alternative_ban_action_unsettime = 15; /* 15min */
    371 	init_default_channel_flood_profiles();
    372 }
    373 
    374 int floodprot_config_test_set_block(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
    375 {
    376 	int errors = 0;
    377 
    378 	if (type != CONFIG_SET)
    379 		return 0;
    380 
    381 	if (!strcmp(ce->name, "modef-default-unsettime"))
    382 	{
    383 		if (!ce->value)
    384 		{
    385 			config_error_empty(ce->file->filename, ce->line_number,
    386 				"set", ce->name);
    387 			errors++;
    388 		} else {
    389 			int v = atoi(ce->value);
    390 			if ((v <= 0) || (v > 255))
    391 			{
    392 				config_error("%s:%i: set::modef-default-unsettime: value '%d' out of range (should be 1-255)",
    393 					ce->file->filename, ce->line_number, v);
    394 				errors++;
    395 			}
    396 		}
    397 	} else
    398 	if (!strcmp(ce->name, "modef-max-unsettime"))
    399 	{
    400 		if (!ce->value)
    401 		{
    402 			config_error_empty(ce->file->filename, ce->line_number,
    403 				"set", ce->name);
    404 			errors++;
    405 		} else {
    406 			int v = atoi(ce->value);
    407 			if ((v <= 0) || (v > 255))
    408 			{
    409 				config_error("%s:%i: set::modef-max-unsettime: value '%d' out of range (should be 1-255)",
    410 					ce->file->filename, ce->line_number, v);
    411 				errors++;
    412 			}
    413 		}
    414 	} else
    415 	if (!strcmp(ce->name, "modef-boot-delay"))
    416 	{
    417 		config_error("%s:%i: set::modef-boot-delay is now called set::anti-flood::channel::boot-delay. "
    418 		             "See https://www.unrealircd.org/docs/Channel_anti-flood_settings#config",
    419 		             ce->file->filename, ce->line_number);
    420 		errors++;
    421 	} else
    422 	{
    423 		/* Not handled by us */
    424 		return 0;
    425 	}
    426 
    427 	*errs = errors;
    428 	return errors ? -1 : 1;
    429 }
    430 
    431 int floodprot_config_run_set_block(ConfigFile *cf, ConfigEntry *ce, int type)
    432 {
    433 	if (type != CONFIG_SET)
    434 		return 0;
    435 
    436 	if (!strcmp(ce->name, "modef-default-unsettime"))
    437 		cfg.modef_default_unsettime = (unsigned char)atoi(ce->value);
    438 	else if (!strcmp(ce->name, "modef-max-unsettime"))
    439 		cfg.modef_max_unsettime = (unsigned char)atoi(ce->value);
    440 	else
    441 		return 0; /* not handled by us */
    442 
    443 	return 1;
    444 }
    445 
    446 /** Check if 'str' is a flood profile name
    447  */
    448 int valid_flood_profile_name(const char *str)
    449 {
    450 	if (strlen(str) > 24)
    451 		return 0;
    452 	for (; *str; str++)
    453 		if (!islower(*str) && !isdigit(*str) && !strchr("_-", *str))
    454 			return 0;
    455 	return 1;
    456 }
    457 
    458 int floodprot_config_test_antiflood_block(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
    459 {
    460 	int errors = 0;
    461 	ConfigEntry *cep;
    462 
    463 	/* We only deal with set::anti-flood::channel */
    464 	if ((type != CONFIG_SET_ANTI_FLOOD) || strcmp(ce->parent->name, "channel"))
    465 		return 0;
    466 
    467 	for (; ce; ce = ce->next)
    468 	{
    469 		if (!strcmp(ce->name, "default-profile"))
    470 		{
    471 			if (!ce->value)
    472 			{
    473 				config_error_noname(ce->file->filename, ce->line_number,
    474 				                    "set::anti-flood::channel::default-profile");
    475 				errors++;
    476 				continue;
    477 			}
    478 		} else
    479 		if (!strcmp(ce->name, "boot-delay") || !strcmp(ce->name, "split-delay"))
    480 		{
    481 			if (!ce->value)
    482 			{
    483 				config_error_empty(ce->file->filename, ce->line_number,
    484 					"set", ce->name);
    485 				errors++;
    486 			} else {
    487 				long v = config_checkval(ce->value, CFG_TIME);
    488 				if ((v < 0) || (v > 600))
    489 				{
    490 					config_error("%s:%i: set::anti-flood::channel::%s: value '%ld' out of range (should be 0-600)",
    491 						ce->file->filename, ce->line_number,
    492 						ce->name,
    493 						v);
    494 					errors++;
    495 				}
    496 			}
    497 		} else
    498 		if (!strcmp(ce->name, "profile"))
    499 		{
    500 			if (!ce->value)
    501 			{
    502 				config_error_noname(ce->file->filename, ce->line_number,
    503 				                    "set::anti-flood::channel::profile");
    504 				errors++;
    505 				continue;
    506 			}
    507 			if (!valid_flood_profile_name(ce->value))
    508 			{
    509 				config_error("%s:%i: set::anti-flood::channel: profile '%s' name is invalid. "
    510 				             "Name can be 24 characters max and may only contain characters a-z, 0-9, _ and -",
    511 				             ce->file->filename, ce->line_number, ce->value);
    512 				errors++;
    513 				continue;
    514 			}
    515 			for (cep = ce->items; cep; cep = cep->next)
    516 			{
    517 				if (!strcmp(cep->name, "flood-mode"))
    518 				{
    519 					ChannelFloodProtection fld;
    520 					const char *err;
    521 
    522 					if (!cep->value)
    523 					{
    524 						config_error("%s:%i: set::anti-flood::channel::profile %s::flood-mode has no value",
    525 						             cep->file->filename, cep->line_number, ce->value);
    526 						errors++;
    527 						continue;
    528 					}
    529 					memset(&fld, 0, sizeof(fld));
    530 					if (!parse_channel_mode_flood(cep->value, &fld, 1, NULL, &err))
    531 					{
    532 						config_error("%s:%i: set::anti-flood::channel::profile %s::flood-mode: %s",
    533 						             cep->file->filename, cep->line_number,
    534 						             ce->value,
    535 						             cep->value);
    536 						errors++;
    537 					} else if (!BadPtr(err))
    538 					{
    539 						config_warn("%s:%i: set::anti-flood::channel::profile %s::flood-mode: %s",
    540 						             cep->file->filename, cep->line_number,
    541 						             ce->value,
    542 						             err);
    543 					}
    544 					if (fld.limit[CHFLD_TEXT] || fld.limit[CHFLD_REPEAT])
    545 					{
    546 						config_error("%s:%i: set::anti-flood::channel::profile %s::flood-mode: "
    547 						             "subtypes 't' and 'r' are not supported for +F profiles at the moment.",
    548 						             cep->file->filename, cep->line_number,
    549 						             ce->value);
    550 						errors++;
    551 					}
    552 				} else {
    553 					config_error_unknown(cep->file->filename, cep->line_number,
    554 							     "set::anti-flood::channel::profile", cep->name);
    555 					errors++;
    556 				}
    557 			}
    558 		} else
    559 		{
    560 			config_error_unknown(ce->file->filename, ce->line_number,
    561 			                     "set::anti-flood::channel", ce->name);
    562 			errors++;
    563 		}
    564 	}
    565 
    566 	*errs = errors;
    567 	return errors ? -2 : 2;
    568 }
    569 
    570 int floodprot_config_run_antiflood_block(ConfigFile *cf, ConfigEntry *ce, int type)
    571 {
    572 	ConfigEntry *cep;
    573 
    574 	/* We only deal with set::anti-flood::channel */
    575 	if ((type != CONFIG_SET_ANTI_FLOOD) || strcmp(ce->parent->name, "channel"))
    576 		return 0;
    577 
    578 	for (; ce; ce = ce->next)
    579 	{
    580 		if (!strcmp(ce->name, "default-profile"))
    581 		{
    582 			safe_strdup(cfg.default_profile, ce->value);
    583 		} else
    584 		if (!strcmp(ce->name, "boot-delay"))
    585 		{
    586 			cfg.boot_delay = config_checkval(ce->value, CFG_TIME);
    587 		} else
    588 		if (!strcmp(ce->name, "split-delay"))
    589 		{
    590 			cfg.split_delay = config_checkval(ce->value, CFG_TIME);
    591 		} else
    592 		if (!strcmp(ce->name, "profile"))
    593 		{
    594 			for (cep = ce->items; cep; cep = cep->next)
    595 			{
    596 				if (!strcmp(cep->name, "flood-mode"))
    597 					set_channel_flood_profile(ce->value, cep->value);
    598 			}
    599 		}
    600 	}
    601 	return 2;
    602 }
    603 
    604 FloodType *find_floodprot_by_letter(char c)
    605 {
    606 	int i;
    607 	for (i=0; i < ARRAY_SIZEOF(floodtypes); i++)
    608 		if (floodtypes[i].letter == c)
    609 			return &floodtypes[i];
    610 
    611 	return NULL;
    612 }
    613 
    614 FloodType *find_floodprot_by_index(Flood index)
    615 {
    616 	int i;
    617 	for (i=0; i < ARRAY_SIZEOF(floodtypes); i++)
    618 		if (floodtypes[i].index == index)
    619 			return &floodtypes[i];
    620 
    621 	return NULL;
    622 }
    623 
    624 ChannelFloodProtection *get_channel_flood_profile(const char *name)
    625 {
    626 	ChannelFloodProfile *f;
    627 
    628 	for (f = channel_flood_profiles; f; f = f->next)
    629 		if (!strcasecmp(f->settings.profile, name))
    630 			return &f->settings;
    631 
    632 	return NULL;
    633 }
    634 
    635 /** Helper function for parse_channel_mode_flood() */
    636 int parse_channel_mode_flood_failed(const char **error_out, ChannelFloodProtection *fld, const char *fmt, ...)
    637 {
    638 	static char retbuf[512];
    639 	int v;
    640 
    641 	va_list vl;
    642 	va_start(vl, fmt);
    643 	vsnprintf(retbuf, sizeof(retbuf), fmt, vl);
    644 	va_end(vl);
    645 
    646 	/* Zero out all settings */
    647 	for (v=0; v < NUMFLD; v++)
    648 	{
    649 		fld->limit[v] = 0;
    650 		fld->action[v] = 0;
    651 		fld->remove_after[v] = 0;
    652 	}
    653 
    654 	if (error_out)
    655 		*error_out = retbuf;
    656 
    657 	return 0;
    658 }
    659 
    660 int floodprot_valid_alternate_action(char action, FloodType *floodtype)
    661 {
    662 	Cmode *cm;
    663 
    664 	/* Built-in actions */
    665 	if (strchr(floodtype->actions, action))
    666 		return 1;
    667 
    668 	cm = find_channel_mode_handler(action);
    669 	if (cm && cm->flood_type_action == floodtype->letter)
    670 		return 1;
    671 
    672 	return 0;
    673 }
    674 
    675 /** Parse channel mode +f string.
    676  * @param param		The parameter string to parse
    677  * @param fld		The setting struct to fill, this MAY already contain data.
    678  * @param strict	If set to 1 then reject invalid setting, used for ex .is_ok().
    679  *			If set to 0 then do your best to make something out of it,
    680  *			and skip invalid stuff for forward-compatibility, eg for .put_param().
    681  * @param client	The client requesting the mode change, can be NULL
    682  *			(used for local vs remote things, not for sending errors)
    683  * @param error		Used for returning the error or warning string, can be NULL.
    684  * @retval 1 On success, although there could still be a warning stored in *error_out.
    685  * @retval 0 On failure, the error will be in *error_out
    686  */
    687 int parse_channel_mode_flood(const char *param, ChannelFloodProtection *fld, int strict, Client *client, const char **error_out)
    688 {
    689 	static char retbuf[512];
    690 	char xbuf[256], c, a, *p, *p2, *x = xbuf+1;
    691 	int v;
    692 	unsigned short breakit;
    693 	unsigned char r;
    694 	FloodType *floodtype;
    695 	Flood index;
    696 	char localclient = (client && MyUser(client)) ? 1 : 0;
    697 	char warn_unknown_flood_type[32];
    698 
    699 	*warn_unknown_flood_type = '\0';
    700 	if (error_out)
    701 		*error_out = NULL;
    702 
    703 	/* always reset settings (l, a, r) */
    704 	for (v=0; v < NUMFLD; v++)
    705 	{
    706 		fld->limit[v] = 0;
    707 		fld->action[v] = 0;
    708 		fld->remove_after[v] = 0;
    709 	}
    710 
    711 	strlcpy(xbuf, param, sizeof(xbuf));
    712 
    713 	if (*xbuf != '[')
    714 		return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (brackets missing)");
    715 
    716 	/* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */
    717 	p2 = strchr(xbuf+1, ']');
    718 	if (!p2)
    719 		return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (brackets missing)");
    720 	*p2 = '\0';
    721 	if (*(p2+1) != ':')
    722 		return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (:XX period missing)");
    723 
    724 	breakit = 0;
    725 	for (x = strtok(xbuf+1, ","); x; x = strtok(NULL, ","))
    726 	{
    727 		/* <number><1 letter>[optional: '#'+1 letter] */
    728 		p = x;
    729 		while(isdigit(*p)) { p++; }
    730 
    731 		/* letter */
    732 		c = *p;
    733 		floodtype = find_floodprot_by_letter(c);
    734 		if (!floodtype)
    735 		{
    736 			strlcat_letter(warn_unknown_flood_type, c, sizeof(warn_unknown_flood_type));
    737 			continue; /* continue instead of break for forward compatability. */
    738 		}
    739 		*p = '\0';
    740 
    741 		/* floodcount (number) */
    742 		v = atoi(x);
    743 		if (strict)
    744 		{
    745 			if ((v < 1) || (v > 999))
    746 				return parse_channel_mode_flood_failed(error_out, fld, "Flood count for '%c' must be 1-999 (got %d)", c, v);
    747 		}
    748 		if (v < 1)
    749 			v = 1;
    750 		if (v > 999)
    751 			v = 999;
    752 		p++;
    753 		a = '\0';
    754 
    755 		/* Removal */
    756 		r = localclient ? MODEF_DEFAULT_UNSETTIME : 0;
    757 		if (*p != '\0')
    758 		{
    759 			if (*p == '#')
    760 			{
    761 				p++;
    762 				a = *p;
    763 				p++;
    764 				if (*p != '\0')
    765 				{
    766 					int tv;
    767 					tv = atoi(p);
    768 					if (tv <= 0)
    769 						tv = 0; /* (ignored) */
    770 					if (tv > 255)
    771 						tv = 255; /* always max limit, as it is a char */
    772 					if (strict && localclient && (tv > MODEF_MAX_UNSETTIME))
    773 						tv = MODEF_MAX_UNSETTIME;
    774 					r = (unsigned char)tv;
    775 				}
    776 			}
    777 		}
    778 
    779 		index = floodtype->index;
    780 		fld->limit[index] = v;
    781 		if (a && floodprot_valid_alternate_action(a, floodtype))
    782 			fld->action[index] = a;
    783 		else
    784 			fld->action[index] = floodtype->default_action;
    785 		if (!floodtype->timedban_required || (floodtype->timedban_required && timedban_available))
    786 			fld->remove_after[index] = r;
    787 	} /* for */
    788 
    789 	/* parse 'per' */
    790 	p2++;
    791 	if (*p2 != ':')
    792 		return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (:XX period missing)");
    793 	p2++;
    794 	if (!*p2)
    795 		return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (:XX period missing)");
    796 	v = atoi(p2);
    797 	if (v < 1)
    798 		v = 1;
    799 
    800 	/* If new 'per xxx seconds' is smaller than current 'per' then reset timers/counters (t, c) */
    801 	if (v < fld->per)
    802 	{
    803 		int i;
    804 		for (i=0; i < NUMFLD; i++)
    805 		{
    806 			fld->timer[i] = 0;
    807 			fld->counter[i] = 0;
    808 			fld->counter_unknown_users[i] = 0;
    809 		}
    810 	}
    811 	fld->per = v;
    812 
    813 	/* Is anything turned on? (to stop things like '+f []:15' */
    814 	breakit = 1;
    815 	for (v=0; v < NUMFLD; v++)
    816 		if (fld->limit[v])
    817 			breakit=0;
    818 	if (breakit)
    819 	{
    820 		/* Nothing is turned on.. */
    821 		if (*warn_unknown_flood_type)
    822 			return parse_channel_mode_flood_failed(error_out, fld, "Unknown flood type(s) '%s'", warn_unknown_flood_type);
    823 		return parse_channel_mode_flood_failed(error_out, fld, "None of the floodtypes set");
    824 	}
    825 
    826 	/* Finally, this is a warning only */
    827 	if (*warn_unknown_flood_type && error_out)
    828 	{
    829 		snprintf(retbuf, sizeof(retbuf), "Unknown flood type(s) '%s'", warn_unknown_flood_type);
    830 		*error_out = retbuf;
    831 	}
    832 
    833 	return 1;
    834 }
    835 
    836 int cmodef_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what)
    837 {
    838 	if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR))
    839 	{
    840 		if (IsUser(client) && check_channel_access(client, channel, "oaq"))
    841 			return EX_ALLOW;
    842 		if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
    843 			sendnumeric(client, ERR_NOTFORHALFOPS, 'f');
    844 		return EX_DENY;
    845 	} else
    846 	if (type == EXCHK_PARAM)
    847 	{
    848 		ChannelFloodProtection fld;
    849 		const char *err;
    850 
    851 		memset(&fld, 0, sizeof(fld));
    852 		if (!parse_channel_mode_flood(param, &fld, 1, client, &err))
    853 		{
    854 			sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'f', err);
    855 			return EX_DENY;
    856 		} else if (err)
    857 		{
    858 			sendnotice(client, "WARNING: Channel mode +f: %s", err);
    859 			/* fallthrough */
    860 		}
    861 		return EX_ALLOW;
    862 	}
    863 
    864 	/* fallthrough -- should not be used */
    865 	return EX_DENY;
    866 }
    867 
    868 void *cmodef_put_param(void *fld_in, const char *param)
    869 {
    870 	ChannelFloodProtection *fld = (ChannelFloodProtection *)fld_in;
    871 	int v;
    872 
    873 	if (!fld)
    874 		fld = safe_alloc(sizeof(ChannelFloodProtection));
    875 
    876 	parse_channel_mode_flood(param, fld, 0, NULL, NULL);
    877 
    878 	return fld;
    879 }
    880 
    881 const char *cmodef_get_param(void *r_in)
    882 {
    883 	ChannelFloodProtection *r = (ChannelFloodProtection *)r_in;
    884 	static char retbuf[512];
    885 
    886 	if (!r)
    887 		return NULL;
    888 
    889 	channel_modef_string(r, retbuf);
    890 	return retbuf;
    891 }
    892 
    893 /** Convert parameter to something proper.
    894  * NOTE: client may be NULL if called for e.g. set::modes-on-join
    895  */
    896 const char *cmodef_conv_param(const char *param_in, Client *client, Channel *channel)
    897 {
    898 	static char retbuf[256];
    899 	ChannelFloodProtection fld;
    900 	const char *err;
    901 
    902 	memset(&fld, 0, sizeof(fld));
    903 	if (!parse_channel_mode_flood(param_in, &fld, 0, client, &err))
    904 		return NULL;
    905 
    906 	*retbuf = '\0';
    907 	channel_modef_string(&fld, retbuf);
    908 	return retbuf;
    909 }
    910 
    911 int cmodef_free_param(void *r, int soft)
    912 {
    913 	ChannelFloodProtection *fld = (ChannelFloodProtection *)r;
    914 
    915 	if (!fld)
    916 		return 0;
    917 
    918 	if (soft && fld->profile && cfg.default_profile)
    919 	{
    920 		/* Resist freeing */
    921 		if (strcmp(fld->profile, cfg.default_profile))
    922 		{
    923 			/* But reset */
    924 			ChannelFloodProtection *base = get_channel_flood_profile(cfg.default_profile);
    925 			if (!base)
    926 				base = get_channel_flood_profile("normal"); /* fallback, always exists */
    927 			inherit_settings(base, fld);
    928 			safe_strdup(fld->profile, base->profile);
    929 		}
    930 		return 1; /* NO FREE */
    931 	} else
    932 	{
    933 		// TODO: consider cancelling timers just to be sure? or maybe in DEBUGMODE?
    934 		safe_free(fld->profile);
    935 		safe_free(r);
    936 	}
    937 	return 0;
    938 }
    939 
    940 void *cmodef_dup_struct(void *r_in)
    941 {
    942 	ChannelFloodProtection *r = (ChannelFloodProtection *)r_in;
    943 	ChannelFloodProtection *w = safe_alloc(sizeof(ChannelFloodProtection));
    944 
    945 	/* We can copy most members in a lazy way... */
    946 	memcpy(w, r, sizeof(ChannelFloodProtection));
    947 	/* ... except this one.
    948 	 * NOTE: can't use safe_strdup() here because
    949 	 * w->profile = r->profile at this point due
    950 	 * to the memcpy and safe_strdup() would free
    951 	 * it (and thus both).
    952 	 */
    953 	if (r->profile)
    954 		w->profile = raw_strdup(r->profile);
    955 	return (void *)w;
    956 }
    957 
    958 int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx)
    959 {
    960 	ChannelFloodProtection *our = (ChannelFloodProtection *)ourx;
    961 	ChannelFloodProtection *their = (ChannelFloodProtection *)theirx;
    962 	int i;
    963 
    964 	if (compare_floodprot_modes(our, their) == 0)
    965 		return EXSJ_SAME;
    966 
    967 	our->per = MAX(our->per, their->per);
    968 	for (i=0; i < NUMFLD; i++)
    969 	{
    970 		our->limit[i] = MAX(our->limit[i], their->limit[i]);
    971 		our->action[i] = MAX(our->action[i], their->action[i]);
    972 		our->remove_after[i] = MAX(our->remove_after[i], their->remove_after[i]);
    973 	}
    974 
    975 	return EXSJ_MERGE;
    976 }
    977 
    978 void floodprot_show_profiles(Client *client)
    979 {
    980 	ChannelFloodProfile *fld;
    981 	char buf[512];
    982 	int padding;
    983 	int max_length = 0;
    984 
    985 	sendnotice(client, "List of available flood profiles for +F:");
    986 	for (fld = channel_flood_profiles; fld; fld = fld->next)
    987 	{
    988 		int n = strlen(fld->settings.profile);
    989 		if (n > max_length)
    990 			max_length = n;
    991 	}
    992 
    993 	for (fld = channel_flood_profiles; fld; fld = fld->next)
    994 	{
    995 		*buf = '\0';
    996 		channel_modef_string(&fld->settings, buf);
    997 		padding = max_length - strlen(fld->settings.profile);
    998 		sendnotice(client, " %*s%s: %s",
    999 		           padding, "",
   1000 		           fld->settings.profile,
   1001 		           buf);
   1002 	}
   1003 	sendnotice(client, "See also https://www.unrealircd.org/docs/Channel_anti-flood_settings");
   1004 }
   1005 
   1006 int cmodef_profile_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what)
   1007 {
   1008 	if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR))
   1009 	{
   1010 		if (IsUser(client) && check_channel_access(client, channel, "oaq"))
   1011 			return EX_ALLOW;
   1012 		if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
   1013 			sendnumeric(client, ERR_NOTFORHALFOPS, 'f');
   1014 		return EX_DENY;
   1015 	} else
   1016 	if (type == EXCHK_PARAM)
   1017 	{
   1018 		if (get_channel_flood_profile(param))
   1019 			return EX_ALLOW;
   1020 		sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'F', "Invalid flood profile specified for +F");
   1021 		floodprot_show_profiles(client);
   1022 		return EX_DENY;
   1023 	}
   1024 
   1025 	/* fallthrough -- should not be used */
   1026 	return EX_DENY;
   1027 }
   1028 
   1029 void inherit_settings(ChannelFloodProtection *from, ChannelFloodProtection *to)
   1030 {
   1031 	int i;
   1032 
   1033 	/* If new 'per xxx seconds' is smaller than current 'per' then reset timers/counters (t, c) */
   1034 	if (from->per < to->per)
   1035 	{
   1036 		for (i=0; i < NUMFLD; i++)
   1037 		{
   1038 			to->timer[i] = 0;
   1039 			to->counter[i] = 0;
   1040 			to->counter_unknown_users[i] = 0;
   1041 		}
   1042 	}
   1043 
   1044 	/* inherit settings (limit/action/remove_after) */
   1045 	for (i=0; i < NUMFLD; i++)
   1046 	{
   1047 		to->limit[i] = from->limit[i];
   1048 		to->action[i] = from->action[i];
   1049 		to->remove_after[i] = from->remove_after[i];
   1050 	}
   1051 	to->per = from->per;
   1052 }
   1053 
   1054 void *cmodef_profile_put_param(void *fld_in, const char *param)
   1055 {
   1056 	ChannelFloodProtection *fld = (ChannelFloodProtection *)fld_in;
   1057 	ChannelFloodProtection *base;
   1058 	int v;
   1059 
   1060 	if (!fld)
   1061 		fld = safe_alloc(sizeof(ChannelFloodProtection));
   1062 
   1063 	base = get_channel_flood_profile(param);
   1064 	if (!base)
   1065 		base = get_channel_flood_profile("normal"); /* fallback, always exists */
   1066 
   1067 	safe_strdup(fld->profile, param);
   1068 	inherit_settings(base, fld);
   1069 
   1070 	return (void *)fld;
   1071 }
   1072 
   1073 const char *cmodef_profile_get_param(void *r_in)
   1074 {
   1075 	ChannelFloodProtection *r = (ChannelFloodProtection *)r_in;
   1076 	static char retbuf[512];
   1077 
   1078 	if (!r)
   1079 		return NULL;
   1080 
   1081 	strlcpy(retbuf, r->profile ? r->profile : "???", sizeof(retbuf));
   1082 	return retbuf;
   1083 }
   1084 
   1085 /** Convert parameter to something proper.
   1086  * NOTE: client may be NULL if called for e.g. set::modes-on-join
   1087  */
   1088 const char *cmodef_profile_conv_param(const char *param_in, Client *client, Channel *channel)
   1089 {
   1090 	static char retbuf[256];
   1091 	ChannelFloodProtection *fld;
   1092 
   1093 	fld = get_channel_flood_profile(param_in);
   1094 	if (!fld)
   1095 		return NULL;
   1096 
   1097 	strlcpy(retbuf, fld->profile, sizeof(retbuf));
   1098 
   1099 	return retbuf;
   1100 }
   1101 
   1102 int cmodef_profile_sjoin_check(Channel *channel, void *ourx, void *theirx)
   1103 {
   1104 	ChannelFloodProtection *our = (ChannelFloodProtection *)ourx;
   1105 	ChannelFloodProtection *their = (ChannelFloodProtection *)theirx;
   1106 	int i;
   1107 
   1108 	if (!strcmp(our->profile, their->profile))
   1109 		return EXSJ_SAME;
   1110 
   1111 	if (strcmp(our->profile, their->profile) < 0)
   1112 		return EXSJ_THEYWON;
   1113 
   1114 	return EXSJ_WEWON;
   1115 }
   1116 
   1117 int is_floodprot_exempt(Client *client, Channel *channel, char flood_type_letter)
   1118 {
   1119 	Ban *ban;
   1120 	char *p;
   1121 	BanContext *b = safe_alloc(sizeof(BanContext));
   1122 
   1123 	b->client = client;
   1124 	b->channel = channel;
   1125 	b->ban_check_types = BANCHK_MSG;
   1126 
   1127 	for (ban = channel->exlist; ban; ban=ban->next)
   1128 	{
   1129 		char *p, *x;
   1130 		char *matchby;
   1131 		char type[16];
   1132 
   1133 		if (!strncmp(ban->banstr, "~F:", 3))
   1134 			p = ban->banstr + 3;
   1135 		else if (!strncmp(ban->banstr, "~flood:", 7))
   1136 			p = ban->banstr + 7;
   1137 		else
   1138 			continue;
   1139 
   1140 		strlcpy(type, p, sizeof(type));
   1141 		x = strchr(type, ':');
   1142 		if (x)
   1143 			*x = '\0';
   1144 
   1145 		if (!strcmp(type, "*") || strchr(type, flood_type_letter))
   1146 		{
   1147 			matchby = strchr(p, ':');
   1148 			if (!matchby)
   1149 				continue;
   1150 			matchby++;
   1151 
   1152 			b->banstr = matchby;
   1153 			if (ban_check_mask(b))
   1154 			{
   1155 				safe_free(b);
   1156 				return HOOK_ALLOW; /* Yes, user is exempt */
   1157 			}
   1158 		}
   1159 	}
   1160 
   1161 	safe_free(b);
   1162 	return HOOK_CONTINUE; /* No, may NOT bypass. */
   1163 }
   1164 
   1165 int floodprot_join(Client *client, Channel *channel, MessageTag *mtags)
   1166 {
   1167 	/* I'll explain this only once:
   1168 	 * 1. if channel is +f
   1169 	 * 2. local client OR synced server
   1170 	 * 3. server uptime more than XX seconds (if this information is available)
   1171 	 * 4. is not a uline
   1172 	 * call do_floodprot, which will:
   1173 	 * 5. then, increase floodcounter
   1174 	 * 6. if we reached the limit AND only if source was a local client.. do the action (+i).
   1175 	 * Nr 6 is done because otherwise you would have a noticeflood with 'joinflood detected'
   1176 	 * from all servers.
   1177 	 */
   1178 	if (IsFloodLimit(channel) &&
   1179 	    (MyUser(client) || client->uplink->server->flags.synced) &&
   1180 	    (client->uplink->server->boottime && (TStime() - client->uplink->server->boottime >= cfg.boot_delay)) &&
   1181 	    (TStime() - floodprot_splittime >= cfg.split_delay) &&
   1182 	    !IsULine(client))
   1183 	{
   1184 	    do_floodprot(channel, client, CHFLD_JOIN);
   1185 	}
   1186 	return 0;
   1187 }
   1188 
   1189 int cmodef_cleanup_user2(Client *client)
   1190 {
   1191 	return 0;
   1192 }
   1193 
   1194 /** Install a default +F profile, if set::anti-flood::channel::default-profile is set */
   1195 int cmodef_channel_create(Channel *channel)
   1196 {
   1197 	ChannelFloodProtection *base;
   1198 	ChannelFloodProtection *fld;
   1199 
   1200 	if (!cfg.default_profile)
   1201 		return 0;
   1202 
   1203 	base = get_channel_flood_profile(cfg.default_profile);
   1204 	if (!base)
   1205 		base = get_channel_flood_profile("normal"); /* fallback, always exists */
   1206 
   1207 	GETPARASTRUCT(channel, 'F') = fld = safe_alloc(sizeof(ChannelFloodProtection));
   1208 	inherit_settings(base, fld);
   1209 	safe_strdup(fld->profile, base->profile);
   1210 
   1211 	return 0;
   1212 }
   1213 
   1214 int cmodef_channel_destroy(Channel *channel, int *should_destroy)
   1215 {
   1216 	floodprottimer_stopchantimers(channel);
   1217 	return 0;
   1218 }
   1219 
   1220 /* [just a helper for channel_modef_string()] */
   1221 static inline char *chmodefstrhelper(char *buf, char t, char tdef, unsigned short l, unsigned char a, unsigned char r)
   1222 {
   1223 char *p;
   1224 char tmpbuf[16], *p2 = tmpbuf;
   1225 
   1226 	sprintf(buf, "%hd", l);
   1227 	p = buf + strlen(buf);
   1228 	*p++ = t;
   1229 	if (a && ((a != tdef) || r))
   1230 	{
   1231 		*p++ = '#';
   1232 		*p++ = a;
   1233 		if (r)
   1234 		{
   1235 			sprintf(tmpbuf, "%hd", (short)r);
   1236 			while ((*p = *p2++))
   1237 				p++;
   1238 		}
   1239 	}
   1240 	*p++ = ',';
   1241 	return p;
   1242 }
   1243 
   1244 /** returns the channelmode +f string (ie: '[5k,40j]:10').
   1245  * 'retbuf' is suggested to be of size 512, which is more than X times the maximum (for safety).
   1246  */
   1247 char *channel_modef_string(ChannelFloodProtection *x, char *retbuf)
   1248 {
   1249 	int i;
   1250 	char *p = retbuf;
   1251 	FloodType *f;
   1252 
   1253 	*p++ = '[';
   1254 
   1255 	for (i=0; i < ARRAY_SIZEOF(floodtypes); i++)
   1256 	{
   1257 		f = &floodtypes[i];
   1258 		if (x->limit[f->index])
   1259 			p = chmodefstrhelper(p, f->letter, f->default_action, x->limit[f->index], x->action[f->index], x->remove_after[f->index]);
   1260 	}
   1261 
   1262 	if (*(p - 1) == ',')
   1263 		p--;
   1264 	*p++ = ']';
   1265 	sprintf(p, ":%hd", x->per);
   1266 	return retbuf;
   1267 }
   1268 
   1269 ChannelFloodProtection *get_channel_flood_settings(Channel *channel, int what)
   1270 {
   1271 	ChannelFloodProtection *fld;
   1272 
   1273 	if (channel->mode.mode & EXTMODE_FLOODLIMIT)
   1274 	{
   1275 		fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f');
   1276 		if (fld->action[what])
   1277 			return fld;
   1278 	}
   1279 
   1280 	fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'F');
   1281 	if (fld && fld->action[what])
   1282 		return fld;
   1283 
   1284 	return NULL;
   1285 }
   1286 
   1287 int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype)
   1288 {
   1289 	Membership *mb;
   1290 	ChannelFloodProtection *fld;
   1291 	MemberFlood *memberflood;
   1292 	uint64_t msghash;
   1293 	unsigned char is_flooding_text=0, is_flooding_repeat=0;
   1294 	static char errbuf[256];
   1295 
   1296 	/* This is redundant, right? */
   1297 	if (!MyUser(client))
   1298 		return HOOK_CONTINUE;
   1299 
   1300 	if (sendtype == SEND_TYPE_TAGMSG)
   1301 		return 0; // TODO: some TAGMSG specific limit? (1 of 2)
   1302 
   1303 	if (ValidatePermissionsForPath("channel:override:flood",client,NULL,channel,NULL) || !IsFloodLimit(channel) || check_channel_access(client, channel, "hoaq"))
   1304 		return HOOK_CONTINUE;
   1305 
   1306 	if (!(mb = find_membership_link(client->user->channel, channel)))
   1307 		return HOOK_CONTINUE; /* not in channel */
   1308 
   1309 	/* config test rejects having 't' in +F and 'r' in +f or vice versa,
   1310 	 * otherwise we would be screwed here :D.
   1311 	 */
   1312 	fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f');
   1313 
   1314 	if (!fld || !(fld->limit[CHFLD_TEXT] || fld->limit[CHFLD_REPEAT]))
   1315 		return HOOK_CONTINUE;
   1316 
   1317 	if (moddata_membership(mb, mdflood).ptr == NULL)
   1318 	{
   1319 		/* Alloc a new entry if it doesn't exist yet */
   1320 		moddata_membership(mb, mdflood).ptr = safe_alloc(sizeof(MemberFlood));
   1321 	}
   1322 
   1323 	memberflood = (MemberFlood *)moddata_membership(mb, mdflood).ptr;
   1324 
   1325 	if ((TStime() - memberflood->firstmsg) >= fld->per)
   1326 	{
   1327 		/* Reset due to moving into a new time slot */
   1328 		memberflood->firstmsg = TStime();
   1329 		memberflood->nmsg = 1;
   1330 		memberflood->nmsg_repeat = 1;
   1331 		if (fld->limit[CHFLD_REPEAT])
   1332 		{
   1333 			memberflood->lastmsg = gen_floodprot_msghash(*msg);
   1334 			memberflood->prevmsg = 0;
   1335 		}
   1336 		return HOOK_CONTINUE; /* forget about it.. */
   1337 	}
   1338 
   1339 	/* Anti-repeat ('r') */
   1340 	if (fld->limit[CHFLD_REPEAT])
   1341 	{
   1342 		msghash = gen_floodprot_msghash(*msg);
   1343 		if (memberflood->lastmsg)
   1344 		{
   1345 			if ((memberflood->lastmsg == msghash) || (memberflood->prevmsg == msghash))
   1346 			{
   1347 				memberflood->nmsg_repeat++;
   1348 				if (memberflood->nmsg_repeat > fld->limit[CHFLD_REPEAT])
   1349 					is_flooding_repeat = 1;
   1350 			}
   1351 			memberflood->prevmsg = memberflood->lastmsg;
   1352 		}
   1353 		memberflood->lastmsg = msghash;
   1354 	}
   1355 
   1356 	if (fld->limit[CHFLD_TEXT])
   1357 	{
   1358 		/* increase msgs */
   1359 		memberflood->nmsg++;
   1360 		if (memberflood->nmsg > fld->limit[CHFLD_TEXT])
   1361 			is_flooding_text = 1;
   1362 	}
   1363 
   1364 	/* Do we need to take any action? */
   1365 	if (is_flooding_text || is_flooding_repeat)
   1366 	{
   1367 		char mask[256];
   1368 		MessageTag *mtags;
   1369 		int flood_type;
   1370 
   1371 		if ((is_flooding_text && is_floodprot_exempt(client, channel, 't')) ||
   1372 		    (is_flooding_repeat && is_floodprot_exempt(client, channel, 'r')))
   1373 		{
   1374 			/* No action taken */
   1375 			memberflood->nmsg = 0;
   1376 			memberflood->nmsg_repeat = 0;
   1377 			return HOOK_CONTINUE;
   1378 		}
   1379 
   1380 		/* Repeat takes precedence over text flood */
   1381 		if (is_flooding_repeat)
   1382 		{
   1383 			snprintf(errbuf, sizeof(errbuf), "Flooding (Your last message is too similar to previous ones)");
   1384 			flood_type = CHFLD_REPEAT;
   1385 		} else
   1386 		{
   1387 			snprintf(errbuf, sizeof(errbuf), "Flooding (Limit is %i lines per %i seconds)", fld->limit[CHFLD_TEXT], fld->per);
   1388 			flood_type = CHFLD_TEXT;
   1389 		}
   1390 
   1391 		if (fld->action[flood_type] == 'd')
   1392 		{
   1393 			/* Drop the message */
   1394 			*errmsg = errbuf;
   1395 			return HOOK_DENY;
   1396 		}
   1397 
   1398 		if (fld->action[flood_type] == 'b')
   1399 		{
   1400 			/* Ban the user */
   1401 			if (timedban_available && (fld->remove_after[flood_type] > 0))
   1402 			{
   1403 				if (iConf.named_extended_bans)
   1404 					snprintf(mask, sizeof(mask), "~time:%d:*!*@%s", fld->remove_after[flood_type], GetHost(client));
   1405 				else
   1406 					snprintf(mask, sizeof(mask), "~t:%d:*!*@%s", fld->remove_after[flood_type], GetHost(client));
   1407 			} else {
   1408 				snprintf(mask, sizeof(mask), "*!*@%s", GetHost(client));
   1409 			}
   1410 			if (add_listmode(&channel->banlist, &me, channel, mask) == 0)
   1411 			{
   1412 				mtags = NULL;
   1413 				new_message(&me, NULL, &mtags);
   1414 				sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +b %s 0", me.id, channel->name, mask);
   1415 				sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags,
   1416 				    ":%s MODE %s +b %s", me.name, channel->name, mask);
   1417 				free_message_tags(mtags);
   1418 			} /* else.. ban list is full */
   1419 		}
   1420 		mtags = NULL;
   1421 		kick_user(NULL, channel, &me, client, errbuf);
   1422 		*errmsg = errbuf; /* not used, but needs to be set */
   1423 		return HOOK_DENY;
   1424 	}
   1425 	return HOOK_CONTINUE;
   1426 }
   1427 
   1428 int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype)
   1429 {
   1430 	if (!IsFloodLimit(channel) || check_channel_access(client, channel, "hoaq") || IsULine(client))
   1431 		return 0;
   1432 
   1433 	if (sendtype == SEND_TYPE_TAGMSG)
   1434 		return 0; // TODO: some TAGMSG specific limit? (2 of 2)
   1435 
   1436 	/* HINT: don't be so stupid to reorder the items in the if's below.. you'll break things -- Syzop. */
   1437 
   1438 	do_floodprot(channel, client, CHFLD_MSG);
   1439 
   1440 	if ((text[0] == '\001') && strncmp(text+1, "ACTION ", 7))
   1441 		do_floodprot(channel, client, CHFLD_CTCP);
   1442 
   1443 	return 0;
   1444 }
   1445 
   1446 int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment)
   1447 {
   1448 	if (IsFloodLimit(channel) && !IsULine(client))
   1449 		do_floodprot(channel, client, CHFLD_KNOCK);
   1450 	return 0;
   1451 }
   1452 
   1453 int floodprot_nickchange(Client *client, MessageTag *mtags, const char *oldnick)
   1454 {
   1455 	Membership *mp;
   1456 
   1457 	/* Ignore u-lines, as usual */
   1458 	if (IsULine(client))
   1459 		return 0;
   1460 
   1461 	/* Don't count forced nick changes, eg from NickServ */
   1462 	if (find_mtag(mtags, "unrealircd.org/issued-by"))
   1463 		return 0;
   1464 
   1465 	for (mp = client->user->channel; mp; mp = mp->next)
   1466 	{
   1467 		Channel *channel = mp->channel;
   1468 		if (channel && IsFloodLimit(channel) && !check_channel_access_membership(mp, "vhoaq"))
   1469 		{
   1470 			do_floodprot(channel, client, CHFLD_NICK);
   1471 		}
   1472 	}
   1473 	return 0;
   1474 }
   1475 
   1476 void floodprot_chanmode_del_helper(ChannelFloodProtection *fld, char modechar)
   1477 {
   1478 	/* reset joinflood on -i, reset msgflood on -m, etc.. */
   1479 	switch(modechar)
   1480 	{
   1481 		case 'C':
   1482 			fld->counter[CHFLD_CTCP] = 0;
   1483 			fld->counter_unknown_users[CHFLD_CTCP] = 0;
   1484 			break;
   1485 		case 'N':
   1486 			fld->counter[CHFLD_NICK] = 0;
   1487 			fld->counter_unknown_users[CHFLD_NICK] = 0;
   1488 			break;
   1489 		case 'm':
   1490 			fld->counter[CHFLD_MSG] = 0;
   1491 			fld->counter[CHFLD_CTCP] = 0;
   1492 			fld->counter_unknown_users[CHFLD_MSG] = 0;
   1493 			fld->counter_unknown_users[CHFLD_CTCP] = 0;
   1494 			break;
   1495 		case 'K':
   1496 			fld->counter[CHFLD_KNOCK] = 0;
   1497 			fld->counter_unknown_users[CHFLD_KNOCK] = 0;
   1498 			break;
   1499 		case 'i':
   1500 			fld->counter[CHFLD_JOIN] = 0;
   1501 			fld->counter_unknown_users[CHFLD_JOIN] = 0;
   1502 			break;
   1503 		case 'M':
   1504 			fld->counter[CHFLD_MSG] = 0;
   1505 			fld->counter[CHFLD_CTCP] = 0;
   1506 			fld->counter_unknown_users[CHFLD_MSG] = 0;
   1507 			fld->counter_unknown_users[CHFLD_CTCP] = 0;
   1508 			break;
   1509 		case 'R':
   1510 			fld->counter[CHFLD_JOIN] = 0;
   1511 			fld->counter_unknown_users[CHFLD_JOIN] = 0;
   1512 			break;
   1513 		default:
   1514 			break;
   1515 	}
   1516 }
   1517 
   1518 int floodprot_chanmode_del(Channel *channel, int modechar)
   1519 {
   1520 	ChannelFloodProtection *fld;
   1521 
   1522 	if (!IsFloodLimit(channel))
   1523 		return 0;
   1524 
   1525 	fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f');
   1526 	if (fld)
   1527 	{
   1528 		floodprot_chanmode_del_helper(fld, modechar);
   1529 		floodprottimer_del(channel, fld, modechar);
   1530 	}
   1531 
   1532 	fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'F');
   1533 	if (fld)
   1534 	{
   1535 		floodprot_chanmode_del_helper(fld, modechar);
   1536 		floodprottimer_del(channel, fld, modechar);
   1537 	}
   1538 
   1539 	return 0;
   1540 }
   1541 
   1542 RemoveChannelModeTimer *floodprottimer_find(Channel *channel, char mflag)
   1543 {
   1544 	RemoveChannelModeTimer *e;
   1545 
   1546 	for (e=removechannelmodetimer_list; e; e=e->next)
   1547 	{
   1548 		if ((e->channel == channel) && (e->m == mflag))
   1549 			return e;
   1550 	}
   1551 	return NULL;
   1552 }
   1553 
   1554 /** strcat-like */
   1555 void strccat(char *s, char c)
   1556 {
   1557 	for (; *s; s++);
   1558 	*s++ = c;
   1559 	*s++ = '\0';
   1560 }
   1561 
   1562 /*
   1563  * Adds a "remove channelmode set by +f" timer.
   1564  * channel	Channel
   1565  * mflag	Mode flag, eg 'C'
   1566  * when		when it should be removed
   1567  * NOTES:
   1568  * - This function takes care of overwriting of any previous timer
   1569  *   for the same modechar.
   1570  * - The function takes care of channel->mode.floodprot->timers_running,
   1571  *   do not modify it yourself.
   1572  * - channel->mode.floodprot is asumed to be non-NULL.
   1573  */
   1574 void floodprottimer_add(Channel *channel, ChannelFloodProtection *fld, char mflag, time_t when)
   1575 {
   1576 	RemoveChannelModeTimer *e = NULL;
   1577 	unsigned char add=1;
   1578 
   1579 	if (strchr(fld->timers_running, mflag))
   1580 	{
   1581 		/* Already exists... */
   1582 		e = floodprottimer_find(channel, mflag);
   1583 		if (e)
   1584 			add = 0;
   1585 	}
   1586 
   1587 	if (!strchr(fld->timers_running, mflag))
   1588 	{
   1589 		if (strlen(fld->timers_running)+1 >= sizeof(fld->timers_running))
   1590 		{
   1591 			unreal_log(ULOG_WARNING, "flood", "BUG_FLOODPROTTIMER_ADD", NULL,
   1592 			           "[BUG] floodprottimer_add: too many timers running for $channel ($timers_running)",
   1593 			           log_data_channel("channel", channel),
   1594 			           log_data_string("timers_running", fld->timers_running));
   1595 			return;
   1596 		}
   1597 		strccat(fld->timers_running, mflag); /* bounds already checked ^^ */
   1598 	}
   1599 
   1600 	if (add)
   1601 		e = safe_alloc(sizeof(RemoveChannelModeTimer));
   1602 
   1603 	e->channel = channel;
   1604 	e->m = mflag;
   1605 	e->when = when;
   1606 
   1607 	if (add)
   1608 		AddListItem(e, removechannelmodetimer_list);
   1609 }
   1610 
   1611 void floodprottimer_del(Channel *channel, ChannelFloodProtection *fld, char mflag)
   1612 {
   1613 	RemoveChannelModeTimer *e;
   1614 
   1615 	if (fld && !strchr(fld->timers_running, mflag))
   1616 		return; /* nothing to remove.. */
   1617 	e = floodprottimer_find(channel, mflag);
   1618 	if (!e)
   1619 		return;
   1620 
   1621 	DelListItem(e, removechannelmodetimer_list);
   1622 	safe_free(e);
   1623 
   1624 	if (fld)
   1625         {
   1626                 char newtf[MAXCHMODEFACTIONS+1];
   1627                 char *i, *o;
   1628                 for (i=fld->timers_running, o=newtf; *i; i++)
   1629                         if (*i != mflag)
   1630                                 *o++ = *i;
   1631                 *o = '\0';
   1632                 strcpy(fld->timers_running, newtf); /* always shorter (or equal) */
   1633         }
   1634 }
   1635 
   1636 EVENT(modef_event)
   1637 {
   1638 	RemoveChannelModeTimer *e, *e_next;
   1639 	time_t now;
   1640 
   1641 	now = TStime();
   1642 
   1643 	for (e = removechannelmodetimer_list; e; e = e_next)
   1644 	{
   1645 		e_next = e->next;
   1646 		if (e->when <= now)
   1647 		{
   1648 			/* Remove chanmode... */
   1649 			Cmode_t extmode = get_extmode_bitbychar(e->m);
   1650 
   1651 			if (extmode && (e->channel->mode.mode & extmode))
   1652 			{
   1653 				MessageTag *mtags = NULL;
   1654 
   1655 				new_message(&me, NULL, &mtags);
   1656 				sendto_server(NULL, 0, 0, mtags, ":%s MODE %s -%c 0", me.id, e->channel->name, e->m);
   1657 				sendto_channel(e->channel, &me, NULL, 0, 0, SEND_LOCAL, mtags,
   1658 				               ":%s MODE %s -%c",
   1659 				               me.name, e->channel->name, e->m);
   1660 				free_message_tags(mtags);
   1661 				e->channel->mode.mode &= ~extmode;
   1662 			}
   1663 
   1664 			/* And delete... */
   1665 			DelListItem(e, removechannelmodetimer_list);
   1666 			safe_free(e);
   1667 		}
   1668 	}
   1669 }
   1670 
   1671 void floodprottimer_stopchantimers(Channel *channel)
   1672 {
   1673 	RemoveChannelModeTimer *e, *e_next;
   1674 
   1675 	for (e = removechannelmodetimer_list; e; e = e_next)
   1676 	{
   1677 		e_next = e->next;
   1678 		if (e->channel == channel)
   1679 		{
   1680 			DelListItem(e, removechannelmodetimer_list);
   1681 			safe_free(e);
   1682 		}
   1683 	}
   1684 }
   1685 
   1686 int do_floodprot(Channel *channel, Client *client, int what)
   1687 {
   1688 	ChannelFloodProtection *fld = get_channel_flood_settings(channel, what);
   1689 	FloodType *floodtype = find_floodprot_by_index(what);
   1690 	char unknown_user;
   1691 
   1692 	if (!fld)
   1693 		return 0; /* no +f active */
   1694 
   1695 	if (floodtype && is_floodprot_exempt(client, channel, floodtype->letter))
   1696 		return 0; /* exempt: not counted and no action taken */
   1697 
   1698 	unknown_user = user_allowed_by_security_group_name(client, "known-users") ? 0 : 1;
   1699 
   1700 	if (fld->limit[what])
   1701 	{
   1702 		if (TStime() - fld->timer[what] >= fld->per)
   1703 		{
   1704 			/* reset */
   1705 			fld->timer[what] = TStime();
   1706 			fld->counter[what] = 1;
   1707 			fld->counter_unknown_users[what] = unknown_user;
   1708 		} else
   1709 		{
   1710 			fld->counter[what]++;
   1711 
   1712 			if (unknown_user)
   1713 				fld->counter_unknown_users[what]++;
   1714 
   1715 			if ((fld->counter[what] > fld->limit[what]) &&
   1716 			    (TStime() - fld->timer[what] < fld->per))
   1717 			{
   1718 				if (MyUser(client))
   1719 					do_floodprot_action(channel, what);
   1720 				return 1; /* flood detected! */
   1721 			}
   1722 		}
   1723 	}
   1724 	return 0;
   1725 }
   1726 
   1727 /** Helper for do_floodprot_action() - standard action +i/+R/etc.. */
   1728 void do_floodprot_action_standard(Channel *channel, int what, FloodType *floodtype, Cmode_t extmode, char m)
   1729 {
   1730 	ChannelFloodProtection *fld = get_channel_flood_settings(channel, what);
   1731 	char comment[512], target[CHANNELLEN + 8];
   1732 	MessageTag *mtags;
   1733 	const char *text = floodtype->description;
   1734 
   1735 	/* First the notice to the chanops */
   1736 	mtags = NULL;
   1737 	new_message(&me, NULL, &mtags);
   1738 	ircsnprintf(comment, sizeof(comment), "*** Channel %s detected (limit is %d per %d seconds), setting mode +%c",
   1739 		text, fld->limit[what], fld->per, m);
   1740 	ircsnprintf(target, sizeof(target), "%%%s", channel->name);
   1741 	sendto_channel(channel, &me, NULL, "ho",
   1742 		       0, SEND_ALL, mtags,
   1743 		       ":%s NOTICE %s :%s", me.name, target, comment);
   1744 	free_message_tags(mtags);
   1745 
   1746 	/* Then the MODE broadcast */
   1747 	mtags = NULL;
   1748 	new_message(&me, NULL, &mtags);
   1749 	sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +%c 0", me.id, channel->name, m);
   1750 	sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +%c", me.name, channel->name, m);
   1751 	free_message_tags(mtags);
   1752 
   1753 	/* Actually set the mode internally */
   1754 	channel->mode.mode |= extmode;
   1755 
   1756 	/* Add remove-chanmode timer */
   1757 	if (fld->remove_after[what])
   1758 	{
   1759 		floodprottimer_add(channel, fld, m, TStime() + ((long)fld->remove_after[what] * 60) - 5);
   1760 		/* (since the floodprot timer event is called every 10s, we do -5 here so the accurancy will
   1761 		 *  be -5..+5, without it it would be 0..+10.)
   1762 		 */
   1763 	}
   1764 }
   1765 
   1766 /** Helper for do_floodprot_action() - alternative action like +b ~security-group:unknown-users */
   1767 int do_floodprot_action_alternative(Channel *channel, int what, FloodType *floodtype)
   1768 {
   1769 	ChannelFloodProtection *fld = get_channel_flood_settings(channel, what);
   1770 	char ban[512];
   1771 	char comment[512], target[CHANNELLEN + 8];
   1772 	MessageTag *mtags;
   1773 	const char *text = floodtype->description;
   1774 
   1775 	snprintf(ban, sizeof(ban), "~time:%d:%s",
   1776 	         fld->remove_after[what] ? fld->remove_after[what] : cfg.modef_alternative_ban_action_unsettime,
   1777 	         floodtype->alternative_ban_action);
   1778 
   1779 	/* Add the ban internally */
   1780 	if (add_listmode(&channel->banlist, &me, channel, ban) == -1)
   1781 		return 0; /* ban list full (or ban already exists) */
   1782 
   1783 	/* First the notice to the chanops */
   1784 	mtags = NULL;
   1785 	new_message(&me, NULL, &mtags);
   1786 	ircsnprintf(comment, sizeof(comment),
   1787 	            "*** Channel %s detected (limit is %d per %d seconds), "
   1788 	            "mostly caused by 'unknown-users', setting mode +b %s",
   1789 		text, fld->limit[what], fld->per, ban);
   1790 	ircsnprintf(target, sizeof(target), "%%%s", channel->name);
   1791 	sendto_channel(channel, &me, NULL, "ho",
   1792 		       0, SEND_ALL, mtags,
   1793 		       ":%s NOTICE %s :%s", me.name, target, comment);
   1794 	free_message_tags(mtags);
   1795 
   1796 	/* Then the MODE broadcast */
   1797 	mtags = NULL;
   1798 	new_message(&me, NULL, &mtags);
   1799 	sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +b %s 0", me.id, channel->name, ban);
   1800 	sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +b %s", me.name, channel->name, ban);
   1801 	free_message_tags(mtags);
   1802 
   1803 	return 1;
   1804 }
   1805 
   1806 
   1807 void do_floodprot_action(Channel *channel, int what)
   1808 {
   1809 	Cmode_t extmode = 0;
   1810 	ChannelFloodProtection *fld = get_channel_flood_settings(channel, what);
   1811 	FloodType *floodtype = find_floodprot_by_index(what);
   1812 	char ban_exists;
   1813 	double perc;
   1814 	char m;
   1815 
   1816 	if (!fld || !floodtype)
   1817 		return;
   1818 
   1819 	/* For drop action we don't actually have to do anything here, but we still have to prevent Unreal
   1820 	 * from setting chmode +d (which is useless against floods anyways) =]
   1821 	 */
   1822 	if (fld->action[what] == 'd')
   1823 		return;
   1824 
   1825 	m = fld->action[what];
   1826 	if (!m)
   1827 		return;
   1828 
   1829 	extmode = get_extmode_bitbychar(m);
   1830 	if (!extmode)
   1831 		return;
   1832 
   1833 	if (extmode && (channel->mode.mode & extmode))
   1834 		return; /* channel mode is already set, so nothing to do */
   1835 
   1836 	/* Do we have other options, instead of setting the channel +i/etc ? */
   1837 	if (floodtype->alternative_ban_action)
   1838 	{
   1839 		/* The 'ban_exists' assignments and checks below are carefully ordered and checked.
   1840 		 * Don't try to "optimize" this code by rearranging or scratching certain checks
   1841 		 * or assignments! -- Syzop
   1842 		 */
   1843 		ban_exists = ban_exists_ignore_time(channel->banlist, floodtype->alternative_ban_action);
   1844 
   1845 		if (!ban_exists)
   1846 		{
   1847 			/* Calculate the percentage of unknown-users that is responsible for the action trigger */
   1848 			perc = ((double)fld->counter_unknown_users[what] / (double)fld->counter[what])*100;
   1849 			if (perc >= cfg.modef_alternate_action_percentage_threshold)
   1850 			{
   1851 				/* ACTION: We need to add the ban (+b) */
   1852 				ban_exists = do_floodprot_action_alternative(channel, what, floodtype);
   1853 			}
   1854 		}
   1855 
   1856 		/* Now recheck, before we fallback to do_floodprot_action_standard() below,
   1857 		 * taking into account that all unknown-users are banned now
   1858 		 * or will be banned (eg: some actions still go through because of lag
   1859 		 * between servers).
   1860 		 */
   1861 		if (ban_exists && (fld->counter[what] - fld->counter_unknown_users[what] <= fld->limit[what]))
   1862 			return; /* flood limit not reached by known-users group */
   1863 	}
   1864 
   1865 	/* ACTION: We need to set the channel mode */
   1866 	do_floodprot_action_standard(channel, what, floodtype, extmode, m);
   1867 }
   1868 
   1869 uint64_t gen_floodprot_msghash(const char *text)
   1870 {
   1871 	int i;
   1872 	int is_ctcp, is_action;
   1873 	char *plaintext;
   1874 	size_t len;
   1875 
   1876 	is_ctcp = is_action = 0;
   1877 	// Remove any control chars (colours/bold/CTCP/etc) and convert it to lowercase before hashing it
   1878 	if (text[0] == '\001')
   1879 	{
   1880 		if (!strncmp(text + 1, "ACTION ", 7))
   1881 			is_action = 1;
   1882 		else
   1883 			is_ctcp = 1;
   1884 	}
   1885 	plaintext = (char *)StripControlCodes(text);
   1886 	for (i = 0; plaintext[i]; i++)
   1887 	{
   1888 		// Don't need to bother with non-printables and various symbols and numbers
   1889 		if (plaintext[i] > 64)
   1890 			plaintext[i] = tolower(plaintext[i]);
   1891 	}
   1892 	if (is_ctcp || is_action)
   1893 	{
   1894 		// Remove the \001 chars around the message
   1895 		if ((len = strlen(plaintext)) && plaintext[len - 1] == '\001')
   1896 			plaintext[len - 1] = '\0';
   1897 		plaintext++;
   1898 		if (is_action)
   1899 			plaintext += 7;
   1900 	}
   1901 
   1902 	return siphash(text, floodprot_msghash_key);
   1903 }
   1904 
   1905 // FIXME: REMARK: make sure you can only do a +f/-f once (latest in line wins).
   1906 
   1907 /* Checks if 2 ChannelFloodProtection modes (chmode +f) are different.
   1908  * This is a bit more complicated than 1 simple memcmp(a,b,..) because
   1909  * counters are also stored in this struct so we have to do
   1910  * it manually :( -- Syzop.
   1911  */
   1912 static int compare_floodprot_modes(ChannelFloodProtection *a, ChannelFloodProtection *b)
   1913 {
   1914 	if (memcmp(a->limit, b->limit, sizeof(a->limit)) ||
   1915 	    memcmp(a->action, b->action, sizeof(a->action)) ||
   1916 	    memcmp(a->remove_after, b->remove_after, sizeof(a->remove_after)))
   1917 		return 1;
   1918 	else
   1919 		return 0;
   1920 }
   1921 
   1922 void memberflood_free(ModData *md)
   1923 {
   1924 	/* We don't have any struct members (anymore) that need freeing */
   1925 	safe_free(md->ptr);
   1926 }
   1927 
   1928 int floodprot_stats(Client *client, const char *flag)
   1929 {
   1930 	if (*flag != 'S')
   1931 		return 0;
   1932 
   1933 	sendtxtnumeric(client, "modef-default-unsettime: %hd", (unsigned short)MODEF_DEFAULT_UNSETTIME);
   1934 	sendtxtnumeric(client, "modef-max-unsettime: %hd", (unsigned short)MODEF_MAX_UNSETTIME);
   1935 	return 1;
   1936 }
   1937 
   1938 /** Admin unloading the floodprot module for good. Bad. */
   1939 void floodprot_free_removechannelmodetimer_list(ModData *m)
   1940 {
   1941 	RemoveChannelModeTimer *e, *e_next;
   1942 
   1943 	for (e=removechannelmodetimer_list; e; e=e_next)
   1944 	{
   1945 		e_next = e->next;
   1946 		safe_free(e);
   1947 	}
   1948 }
   1949 
   1950 void floodprot_free_msghash_key(ModData *m)
   1951 {
   1952 	safe_free(floodprot_msghash_key);
   1953 }
   1954 
   1955 CMD_OVERRIDE_FUNC(floodprot_override_mode)
   1956 {
   1957 	if (MyUser(client) && (parc == 3) &&
   1958 	    (parv[1][0] == '#') &&
   1959 	    (!strcasecmp(parv[2], "f") || !strcasecmp(parv[2], "+f")))
   1960 	{
   1961 		/* Query (not set!) request for #channel */
   1962 		Channel *channel = find_channel(parv[1]);
   1963 		ChannelFloodProtection *profile, *advanced;
   1964 		char buf[512];
   1965 
   1966 		if (!channel)
   1967 		{
   1968 			sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]);
   1969 			return;
   1970 		}
   1971 
   1972 		advanced = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f');
   1973 		profile = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'F');
   1974 		if (!advanced && !profile)
   1975 		{
   1976 			sendnotice(client, "No channel mode +f/+F is active on %s", channel->name);
   1977 		} else
   1978 		if (advanced && !profile)
   1979 		{
   1980 			channel_modef_string(advanced, buf);
   1981 			sendnotice(client, "Channel '%s' has effective flood setting '%s' (custom settings via +f)",
   1982 			           channel->name, buf);
   1983 		} else
   1984 		if (profile && !advanced)
   1985 		{
   1986 			channel_modef_string(profile, buf);
   1987 			sendnotice(client, "Channel '%s' has effective flood setting '%s' (flood profile '%s')",
   1988 			           channel->name, buf, profile->profile);
   1989 		} else {
   1990 			/* Both +f and +F are set */
   1991 			int v;
   1992 			ChannelFloodProtection mix;
   1993 			FloodType *t;
   1994 			char overridden[64];
   1995 			*overridden = '\0';
   1996 			memcpy(&mix, profile, sizeof(mix));
   1997 			for (v=0; v < NUMFLD; v++)
   1998 			{
   1999 				if ((advanced->limit[v]>0) && (mix.limit[v]>0))
   2000 				{
   2001 					mix.limit[v] = 0;
   2002 					mix.action[v] = 0;
   2003 					t = find_floodprot_by_index(v);
   2004 					if (t)
   2005 						strlcat_letter(overridden, t->letter, sizeof(overridden));
   2006 				}
   2007 			}
   2008 			channel_modef_string(&mix, buf);
   2009 			if (*overridden)
   2010 			{
   2011 				sendnotice(client, "Channel '%s' uses flood profile '%s', without action(s) '%s' as they are overridden by +f.",
   2012 					   channel->name, profile->profile, overridden);
   2013 				sendnotice(client, "Effective flood setting via +F: '%s'", buf);
   2014 			} else {
   2015 				sendnotice(client, "Channel '%s' has effective flood setting '%s' (flood profile '%s')",
   2016 					   channel->name, buf, profile->profile);
   2017 			}
   2018 			channel_modef_string(advanced, buf);
   2019 			sendnotice(client, "Plus flood setting via +f: '%s'", buf);
   2020 		}
   2021 		sendnotice(client, "-");
   2022 		floodprot_show_profiles(client);
   2023 		return;
   2024 	}
   2025 
   2026 	CALL_NEXT_COMMAND_OVERRIDE();
   2027 }
   2028 
   2029 int floodprot_server_quit(Client *client, MessageTag *mtags)
   2030 {
   2031 	if (!IsULine(client))
   2032 		floodprot_splittime = TStime();
   2033 	return 0;
   2034 }
   2035 
   2036 void reapply_profiles(void)
   2037 {
   2038 	Channel *channel;
   2039 
   2040 	for (channel = channels; channel; channel=channel->nextch)
   2041 	{
   2042 		ChannelFloodProtection *fld = GETPARASTRUCT(channel, 'F');
   2043 		ChannelFloodProtection *base;
   2044 
   2045 		if (channel->mode.mode & EXTMODE_FLOOD_PROFILE)
   2046 		{
   2047 			/* Channel is +F: simply re-apply the setting the +F <profile> */
   2048 			base = get_channel_flood_profile(fld->profile);
   2049 			if (base)
   2050 				inherit_settings(base, fld);
   2051 		} else
   2052 		{
   2053 			/* Channel is -F */
   2054 			if (cfg.default_profile)
   2055 			{
   2056 				/* We are -F and set::anti-flood::channel::default-profile
   2057 				 * is configured, so apply it or re-apply it.
   2058 				 */
   2059 				if (!fld)
   2060 				{
   2061 					cmodef_channel_create(channel);
   2062 				} else {
   2063 					base = get_channel_flood_profile(cfg.default_profile);
   2064 					if (base)
   2065 					{
   2066 						inherit_settings(base, fld);
   2067 						safe_strdup(fld->profile, cfg.default_profile);
   2068 					}
   2069 				}
   2070 			} else {
   2071 				if (fld)
   2072 				{
   2073 					/* Not +F, previously we had a default profile
   2074 					 * and now set::anti-flood::channel::default-profile
   2075 					 * is unset, so remove the flood profile.
   2076 					 */
   2077 					cmodef_free_param(fld, 0);
   2078 					GETPARASTRUCT(channel, 'F') = NULL;
   2079 				}
   2080 			}
   2081 		}
   2082 	}
   2083 }
   2084 
   2085 // TODO: handle mismatch of flood profiles between servers
   2086 
   2087 // TODO: perhaps an option to turn off +F in the IRCd without turning off +f ? maybe as a multi-option.