unrealircd

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

api-channelmode.c (30017B)

      1 /************************************************************************
      2  *   IRC - Internet Relay Chat, src/api-channelmode.c
      3  *   (C) 2003-2007 Bram Matthys (Syzop) and the UnrealIRCd Team
      4  *
      5  *   See file AUTHORS in IRC package for additional names of
      6  *   the programmers. 
      7  *
      8  *   This program is free software; you can redistribute it and/or modify
      9  *   it under the terms of the GNU General Public License as published by
     10  *   the Free Software Foundation; either version 1, or (at your option)
     11  *   any later version.
     12  *
     13  *   This program is distributed in the hope that it will be useful,
     14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *   GNU General Public License for more details.
     17  *
     18  *   You should have received a copy of the GNU General Public License
     19  *   along with this program; if not, write to the Free Software
     20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     21  */
     22 
     23 /** @file
     24  * @brief The channel mode API used by modules.
     25  */
     26 
     27 #include "unrealircd.h"
     28 
     29 /** This is the extended channel mode API,
     30  * see also https://www.unrealircd.org/docs/Dev:Channel_Mode_API
     31  * for more information.
     32  * @defgroup ChannelModeAPI Channel mode API
     33  * @{
     34  */
     35 
     36 /** List of all channel modes, their handlers, etc */
     37 Cmode *channelmodes = NULL;
     38 
     39 /** @} */
     40 
     41 /** Channel parameter to slot# mapping - used by GETPARAMSLOT() macro */
     42 MODVAR unsigned char param_to_slot_mapping[256];
     43 /** Extended channel modes in use - used by ISUPPORT/005 numeric only */
     44 char extchmstr[4][64];
     45 
     46 /* Private functions (forward declaration) and variables */
     47 static void make_cmodestr(void);
     48 static char previous_chanmodes[256];
     49 static char previous_prefix[256];
     50 static Cmode *ParamTable[MAXPARAMMODES+1];
     51 static void unload_extcmode_commit(Cmode *cmode);
     52 
     53 /** Create the strings that are used for CHANMODES=a,b,c,d in numeric 005 */
     54 void make_extcmodestr()
     55 {
     56 	char *p;
     57 	Cmode *cm;
     58 	int i;
     59 	
     60 	extchmstr[0][0] = extchmstr[1][0] = extchmstr[2][0] = extchmstr[3][0] = '\0';
     61 	
     62 	/* type 1: lists (like b/e) */
     63 	/* [NOT IMPLEMENTED IN EXTCMODES] */
     64 
     65 	/* type 2: 1 par to set/unset (has .unset_with_param) */
     66 	p = extchmstr[1];
     67 	for (cm=channelmodes; cm; cm = cm->next)
     68 		if (cm->paracount && cm->letter && cm->unset_with_param && (cm->type != CMODE_MEMBER))
     69 			*p++ = cm->letter;
     70 	*p = '\0';
     71 
     72 	/* type 3: 1 param to set, 0 params to unset (does not have .unset_with_param) */
     73 	p = extchmstr[2];
     74 	for (cm=channelmodes; cm; cm = cm->next)
     75 		if (cm->paracount && cm->letter && !cm->unset_with_param)
     76 			*p++ = cm->letter;
     77 	*p = '\0';
     78 	
     79 	/* type 4: paramless modes */
     80 	p = extchmstr[3];
     81 	for (cm=channelmodes; cm; cm = cm->next)
     82 		if (!cm->paracount && cm->letter)
     83 			*p++ = cm->letter;
     84 	*p = '\0';
     85 }
     86 
     87 /** Create the string that is used in numeric 004 */
     88 static void make_cmodestr(void)
     89 {
     90 	Cmode *cm;
     91 	char *p = &cmodestring[0];
     92 	CoreChannelModeTable *tab = &corechannelmodetable[0];
     93 	int i;
     94 	while (tab->mode != 0x0)
     95 	{
     96 		*p = tab->flag;
     97 		p++;
     98 		tab++;
     99 	}
    100 	for (cm=channelmodes; cm; cm = cm->next)
    101 		if (cm->letter)
    102 			*p++ = cm->letter;
    103 	*p = '\0';
    104 }
    105 
    106 /** Check for changes - if any are detected, we broadcast the change */
    107 void extcmodes_check_for_changed_channel_modes(void)
    108 {
    109 	char chanmodes[256];
    110 	ISupport *isup;
    111 
    112 	//sort_cmodes();
    113 	make_cmodestr();
    114 	make_extcmodestr();
    115 
    116 	snprintf(chanmodes, sizeof(chanmodes), "%s%s", CHPAR1, EXPAR1);
    117 	safe_strdup(me.server->features.chanmodes[0], chanmodes);
    118 	snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR2);
    119 	safe_strdup(me.server->features.chanmodes[1], chanmodes);
    120 	snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR3);
    121 	safe_strdup(me.server->features.chanmodes[2], chanmodes);
    122 	snprintf(chanmodes, sizeof(chanmodes), "%s", EXPAR4);
    123 	safe_strdup(me.server->features.chanmodes[3], chanmodes);
    124 
    125 	ircsnprintf(chanmodes, sizeof(chanmodes), "%s,%s,%s,%s",
    126 	            me.server->features.chanmodes[0],
    127 	            me.server->features.chanmodes[1],
    128 	            me.server->features.chanmodes[2],
    129 	            me.server->features.chanmodes[3]);
    130 
    131 	isup = ISupportFind("CHANMODES");
    132 	if (!isup)
    133 	{
    134 		strlcpy(previous_chanmodes, chanmodes, sizeof(previous_chanmodes));
    135 		return; /* not booted yet. then we are done here. */
    136 	}
    137 	
    138 	ISupportSetValue(isup, chanmodes);
    139 	
    140 	if (*previous_chanmodes && strcmp(chanmodes, previous_chanmodes))
    141 	{
    142 		unreal_log(ULOG_INFO, "mode", "CHANNEL_MODES_CHANGED", NULL,
    143 		           "Channel modes changed at runtime: $old_channel_modes -> $new_channel_modes",
    144 		           log_data_string("old_channel_modes", previous_chanmodes),
    145 		           log_data_string("new_channel_modes", chanmodes));
    146 		/* Broadcast change to all (locally connected) servers */
    147 		sendto_server(NULL, 0, 0, NULL, "PROTOCTL CHANMODES=%s", chanmodes);
    148 	}
    149 
    150 	strlcpy(previous_chanmodes, chanmodes, sizeof(previous_chanmodes));
    151 }
    152 
    153 void make_prefix(char **isupport_prefix, char **isupport_statusmsg)
    154 {
    155 	static char prefix[256];
    156 	static char prefix_prefix[256];
    157 	char prefix_modes[256];
    158 	int rank[256];
    159 	Cmode *cm;
    160 	int n;
    161 
    162 	*prefix = *prefix_prefix = *prefix_modes = '\0';
    163 
    164 	for (n=0, cm=channelmodes; cm && n < ARRAY_SIZEOF(rank)-1; cm = cm->next)
    165 	{
    166 		if ((cm->type == CMODE_MEMBER) && cm->letter)
    167 		{
    168 			strlcat_letter(prefix_prefix, cm->prefix, sizeof(prefix_prefix));
    169 			strlcat_letter(prefix_modes, cm->letter, sizeof(prefix_modes));
    170 			rank[n] = cm->rank;
    171 			n++;
    172 		}
    173 	}
    174 
    175 	if (*prefix_prefix)
    176 	{
    177 		int i, j;
    178 		/* Now sort the damn thing */
    179 		for (i=0; i < n; i++)
    180 		{
    181 			for (j=i+1; j < n; j++)
    182 			{
    183 				if (rank[i] < rank[j])
    184 				{
    185 					/* swap */
    186 					char save;
    187 					int save_rank;
    188 					save = prefix_prefix[i];
    189 					prefix_prefix[i] = prefix_prefix[j];
    190 					prefix_prefix[j] = save;
    191 					save = prefix_modes[i];
    192 					prefix_modes[i] = prefix_modes[j];
    193 					prefix_modes[j] = save;
    194 					save_rank = rank[i];
    195 					rank[i] = rank[j];
    196 					rank[j] = save_rank;
    197 				}
    198 			}
    199 		}
    200 		snprintf(prefix, sizeof(prefix), "(%s)%s", prefix_modes, prefix_prefix);
    201 	}
    202 
    203 	*isupport_prefix = prefix;
    204 	*isupport_statusmsg = prefix_prefix;
    205 }
    206 
    207 void extcmodes_check_for_changed_prefixes(void)
    208 {
    209 	ISupport *isup;
    210 	char *prefix, *statusmsg;
    211 
    212 	make_prefix(&prefix, &statusmsg);
    213 	ISupportSet(NULL, "PREFIX", prefix);
    214 	ISupportSet(NULL, "STATUSMSG", statusmsg);
    215 
    216 	if (*previous_prefix && strcmp(prefix, previous_prefix))
    217 	{
    218 		unreal_log(ULOG_INFO, "mode", "PREFIX_CHANGED", NULL,
    219 		           "Prefix changed at runtime: $old_prefix -> $new_prefix",
    220 		           log_data_string("old_prefix", previous_prefix),
    221 		           log_data_string("new_prefix", prefix));
    222 		/* Broadcast change to all (locally connected) servers */
    223 		sendto_server(NULL, 0, 0, NULL, "PROTOCTL PREFIX=%s", prefix);
    224 	}
    225 
    226 	strlcpy(previous_prefix, prefix, sizeof(previous_prefix));
    227 }
    228 
    229 /** Check for changes - if any are detected, we broadcast the change */
    230 void extcmodes_check_for_changes(void)
    231 {
    232 	extcmodes_check_for_changed_channel_modes();
    233 	extcmodes_check_for_changed_prefixes();
    234 }
    235 
    236 /** Initialize the extended channel modes system */
    237 void extcmode_init(void)
    238 {
    239 	memset(&extchmstr, 0, sizeof(extchmstr));
    240 	memset(&param_to_slot_mapping, 0, sizeof(param_to_slot_mapping));
    241 	*previous_chanmodes = '\0';
    242 	*previous_prefix = '\0';
    243 }
    244 
    245 /** Update letter->slot mapping and slot->handler mapping */
    246 void extcmode_para_addslot(Cmode *cm, int slot)
    247 {
    248 	if ((slot < 0) || (slot > MAXPARAMMODES))
    249 		abort();
    250 	cm->param_slot = slot;
    251 	ParamTable[slot] = cm;
    252 	param_to_slot_mapping[cm->letter] = slot;
    253 }
    254 
    255 /** Update letter->slot mapping and slot->handler mapping */
    256 void extcmode_para_delslot(Cmode *cm, int slot)
    257 {
    258 	if ((slot < 0) || (slot > MAXPARAMMODES))
    259 		abort();
    260 	ParamTable[slot] = NULL;
    261 	param_to_slot_mapping[cm->letter] = 0;
    262 }
    263 
    264 void channelmode_add_sorted(Cmode *n)
    265 {
    266 	Cmode *m;
    267 
    268 	if (channelmodes == NULL)
    269 	{
    270 		channelmodes = n;
    271 		return;
    272 	}
    273 
    274 	for (m = channelmodes; m; m = m->next)
    275 	{
    276 		if (m->letter == '\0')
    277 			abort();
    278 		if (sort_character_lowercase_before_uppercase(n->letter, m->letter))
    279 		{
    280 			/* Insert us before */
    281 			if (m->prev)
    282 				m->prev->next = n;
    283 			else
    284 				channelmodes = n; /* new head */
    285 			n->prev = m->prev;
    286 
    287 			n->next = m;
    288 			m->prev = n;
    289 			return;
    290 		}
    291 		if (!m->next)
    292 		{
    293 			/* Append us at end */
    294 			m->next = n;
    295 			n->prev = m;
    296 			return;
    297 		}
    298 	}
    299 }
    300 
    301 /** @defgroup ChannelModeAPI Channel mode API
    302  * @{
    303  */
    304 
    305 /** Register a new channel mode (Channel mode API).
    306  * @param module	The module requesting this channel mode (usually: modinfo->handle)
    307  * @param req		Details of the channel mode request
    308  * @param mode		Store the mode value (bit) here on success
    309  * @returns the newly created channel mode, or NULL in case of error.
    310  */
    311 Cmode *CmodeAdd(Module *module, CmodeInfo req, Cmode_t *mode)
    312 {
    313 	int paraslot = -1;
    314 	int existing = 0;
    315 	Cmode *cm;
    316 
    317 	for (cm=channelmodes; cm; cm = cm->next)
    318 	{
    319 		if (cm->letter == req.letter)
    320 		{
    321 			if (cm->unloaded)
    322 			{
    323 				cm->unloaded = 0;
    324 				existing = 1;
    325 				break;
    326 			} else {
    327 				if (module)
    328 					module->errorcode = MODERR_EXISTS;
    329 				return NULL;
    330 			}
    331 		}
    332 	}
    333 
    334 	if (!cm)
    335 	{
    336 		long l, found = 0;
    337 
    338 		if (req.type == CMODE_NORMAL)
    339 		{
    340 			for (l = 1; l < LONG_MAX/2; l *= 2)
    341 			{
    342 				found = 0;
    343 				for (cm=channelmodes; cm; cm = cm->next)
    344 				{
    345 					if (cm->mode == l)
    346 					{
    347 						found = 1;
    348 						break;
    349 					}
    350 				}
    351 				if (!found)
    352 					break;
    353 			}
    354 			/* If 'found' is still true, then we are out of space */
    355 			if (found)
    356 			{
    357 				unreal_log(ULOG_ERROR, "module", "CHANNEL_MODE_OUT_OF_SPACE", NULL,
    358 					   "CmodeAdd: out of space!!!");
    359 				if (module)
    360 					module->errorcode = MODERR_NOSPACE;
    361 				return NULL;
    362 			}
    363 			cm = safe_alloc(sizeof(Cmode));
    364 			cm->letter = req.letter;
    365 			cm->mode = l;
    366 			*mode = cm->mode;
    367 		} else if (req.type == CMODE_MEMBER)
    368 		{
    369 			if (!req.prefix || !req.sjoin_prefix || !req.paracount ||
    370 			    !req.unset_with_param || !req.rank)
    371 			{
    372 				unreal_log(ULOG_ERROR, "module", "CMODEADD_API_ERROR", NULL,
    373 					   "CmodeAdd(): module is missing required information. "
    374 					   "Module: $module_name",
    375 					   log_data_string("module_name", module->header->name));
    376 				module->errorcode = MODERR_INVALID;
    377 				return NULL;
    378 			}
    379 			cm = safe_alloc(sizeof(Cmode));
    380 			cm->letter = req.letter;
    381 		} else {
    382 			abort();
    383 		}
    384 		channelmode_add_sorted(cm);
    385 	}
    386 
    387 	if ((req.paracount == 1) && (req.type == CMODE_NORMAL))
    388 	{
    389 		if (existing)
    390 		{
    391 			/* Re-use parameter slot of the module with the same modechar that is unloading */
    392 			paraslot = cm->param_slot;
    393 		}
    394 		else
    395 		{
    396 			/* Allocate a new one */
    397 			for (paraslot = 0; ParamTable[paraslot]; paraslot++)
    398 			{
    399 				if (paraslot == MAXPARAMMODES - 1)
    400 				{
    401 					unreal_log(ULOG_ERROR, "module", "CHANNEL_MODE_OUT_OF_SPACE", NULL,
    402 						   "CmodeAdd: out of space!!! Place 2.");
    403 					if (module)
    404 						module->errorcode = MODERR_NOSPACE;
    405 					return NULL;
    406 				}
    407 			}
    408 		}
    409 	}
    410 
    411 	cm->letter = req.letter;
    412 	cm->type = req.type;
    413 	cm->prefix = req.prefix;
    414 	cm->sjoin_prefix = req.sjoin_prefix;
    415 	cm->rank = req.rank;
    416 	cm->paracount = req.paracount;
    417 	cm->is_ok = req.is_ok;
    418 	cm->put_param = req.put_param;
    419 	cm->get_param = req.get_param;
    420 	cm->conv_param = req.conv_param;
    421 	cm->free_param = req.free_param;
    422 	cm->dup_struct = req.dup_struct;
    423 	cm->sjoin_check = req.sjoin_check;
    424 	cm->local = req.local;
    425 	cm->unset_with_param = req.unset_with_param;
    426 	cm->flood_type_action = req.flood_type_action;
    427 	cm->owner = module;
    428 	cm->unloaded = 0;
    429 
    430 	if (cm->type == CMODE_NORMAL)
    431 	{
    432 		*mode = cm->mode;
    433 		if (cm->paracount == 1)
    434 			extcmode_para_addslot(cm, paraslot);
    435 	}
    436 
    437 	if (module)
    438 	{
    439 		ModuleObject *cmodeobj = safe_alloc(sizeof(ModuleObject));
    440 		cmodeobj->object.cmode = cm;
    441 		cmodeobj->type = MOBJ_CMODE;
    442 		AddListItem(cmodeobj, module->objects);
    443 		module->errorcode = MODERR_NOERROR;
    444 	}
    445 	return cm;
    446 }
    447 
    448 /** Delete a previously registered channel mode - not called by modules.
    449  * For modules this is done automatically on unload, no need to call this explicitly.
    450  */
    451 void CmodeDel(Cmode *cmode)
    452 {
    453 	if (cmode->owner)
    454 	{
    455 		ModuleObject *cmodeobj;
    456 		for (cmodeobj = cmode->owner->objects; cmodeobj; cmodeobj = cmodeobj->next) {
    457 			if (cmodeobj->type == MOBJ_CMODE && cmodeobj->object.cmode == cmode) {
    458 				DelListItem(cmodeobj, cmode->owner->objects);
    459 				safe_free(cmodeobj);
    460 				break;
    461 			}
    462 		}
    463 		cmode->owner = NULL;
    464 	}
    465 	if (loop.rehashing)
    466 		cmode->unloaded = 1;
    467 	else
    468 		unload_extcmode_commit(cmode);
    469 
    470 }
    471 
    472 /** @} */
    473 
    474 /** After a channel mode is deregistered for sure, unload it completely.
    475  * This is done after a REHASH when no new module has registered the mode.
    476  * Then we can unload it for good. This also sends MODE -.. out etc.
    477  */
    478 static void unload_extcmode_commit(Cmode *cmode)
    479 {
    480 	Channel *channel;
    481 
    482 	if (!cmode)
    483 		return;
    484 
    485 	if (cmode->type == CMODE_NORMAL)
    486 	{
    487 		/* Unset channel mode and send MODE to everyone */
    488 		if (cmode->paracount == 0)
    489 		{
    490 			/* Paramless mode, easy */
    491 			for (channel = channels; channel; channel = channel->nextch)
    492 			{
    493 				if (channel->mode.mode & cmode->mode)
    494 				{
    495 					MessageTag *mtags = NULL;
    496 
    497 					new_message(&me, NULL, &mtags);
    498 					sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags,
    499 						       ":%s MODE %s -%c",
    500 						       me.name, channel->name, cmode->letter);
    501 					sendto_server(NULL, 0, 0, mtags,
    502 						":%s MODE %s -%c 0",
    503 						me.id, channel->name, cmode->letter);
    504 					free_message_tags(mtags);
    505 
    506 					channel->mode.mode &= ~cmode->mode;
    507 				}
    508 			}
    509 		} else
    510 		{
    511 			/* Parameter mode, more complicated */
    512 			for (channel = channels; channel; channel = channel->nextch)
    513 			{
    514 				if (channel->mode.mode & cmode->mode)
    515 				{
    516 					MessageTag *mtags = NULL;
    517 
    518 					new_message(&me, NULL, &mtags);
    519 					if (cmode->unset_with_param)
    520 					{
    521 						const char *param = cmode->get_param(GETPARASTRUCT(channel, cmode->letter));
    522 						sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags,
    523 							       ":%s MODE %s -%c %s",
    524 							       me.name, channel->name, cmode->letter, param);
    525 						sendto_server(NULL, 0, 0, mtags,
    526 							":%s MODE %s -%c %s 0",
    527 							me.id, channel->name, cmode->letter, param);
    528 					} else {
    529 						sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags,
    530 							       ":%s MODE %s -%c",
    531 							       me.name, channel->name, cmode->letter);
    532 						sendto_server(NULL, 0, 0, mtags,
    533 							":%s MODE %s -%c 0",
    534 							me.id, channel->name, cmode->letter);
    535 					}
    536 					free_message_tags(mtags);
    537 
    538 					cmode->free_param(GETPARASTRUCT(channel, cmode->letter), 0);
    539 					channel->mode.mode &= ~cmode->mode;
    540 				}
    541 			}
    542 			extcmode_para_delslot(cmode, cmode->param_slot);
    543 		}
    544 	} else
    545 	if (cmode->type == CMODE_MEMBER)
    546 	{
    547 		for (channel = channels; channel; channel = channel->nextch)
    548 		{
    549 			Member *m;
    550 			for (m = channel->members; m; m = m->next)
    551 			{
    552 				if (strchr(m->member_modes, cmode->letter))
    553 				{
    554 					MessageTag *mtags = NULL;
    555 
    556 					new_message(&me, NULL, &mtags);
    557 					sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags,
    558 						       ":%s MODE %s -%c %s",
    559 						       me.name, channel->name, cmode->letter, m->client->name);
    560 					sendto_server(NULL, 0, 0, mtags,
    561 						":%s MODE %s -%c %s 0",
    562 						me.id, channel->name, cmode->letter, m->client->id);
    563 					free_message_tags(mtags);
    564 					del_member_mode(m->client, channel, cmode->letter);
    565 				}
    566 			}
    567 		}
    568 	}
    569 
    570 	DelListItem(cmode, channelmodes);
    571 	safe_free(cmode);
    572 }
    573 
    574 /** Unload all unused channel modes after a REHASH */
    575 void unload_all_unused_extcmodes(void)
    576 {
    577 	Cmode *cm, *cm_next;
    578 
    579 	for (cm=channelmodes; cm; cm = cm_next)
    580 	{
    581 		cm_next = cm->next;
    582 		if (cm->letter && cm->unloaded)
    583 		{
    584 			unload_extcmode_commit(cm);
    585 		}
    586 	}
    587 
    588 }
    589 
    590 /** @defgroup ChannelModeAPI Channel mode API
    591  * @{
    592  */
    593 
    594 /** Get parameter for a channel mode.
    595  * @param channel	The channel
    596  * @param mode		The mode character (eg: 'f')
    597  */
    598 const char *cm_getparameter(Channel *channel, char mode)
    599 {
    600 	return GETPARAMHANDLERBYLETTER(mode)->get_param(GETPARASTRUCT(channel, mode));
    601 }
    602 
    603 /** Get parameter for a channel mode - special version for SJOIN.
    604  * This version doesn't take a channel, but a mode.mode_params.
    605  * It is only used by SJOIN and should not be used in 3rd party modules.
    606  * @param p	The list, eg oldmode.mode_params
    607  * @param mode	The mode letter
    608  */
    609 const char *cm_getparameter_ex(void **p, char mode)
    610 {
    611 	return GETPARAMHANDLERBYLETTER(mode)->get_param(GETPARASTRUCTEX(p, mode));
    612 }
    613 
    614 /** Set parameter for a channel mode.
    615  * @param channel	The channel
    616  * @param mode		The mode character (eg: 'f')
    617  * @param str		The parameter string
    618  * @note Module users should not use this function directly, it is only used by MODE and SJOIN.
    619  */
    620 void cm_putparameter(Channel *channel, char mode, const char *str)
    621 {
    622 	GETPARASTRUCT(channel, mode) = GETPARAMHANDLERBYLETTER(mode)->put_param(GETPARASTRUCT(channel, mode), str);
    623 }
    624 
    625 /** Free a channel mode parameter.
    626  * @param channel	The channel
    627  * @param mode		The mode character (eg: 'f')
    628  */
    629 void cm_freeparameter(Channel *channel, char mode)
    630 {
    631 	int n = GETPARAMHANDLERBYLETTER(mode)->free_param(GETPARASTRUCT(channel, mode), 1);
    632 	if (n == 0)
    633 		GETPARASTRUCT(channel, mode) = NULL;
    634 }
    635 
    636 
    637 /** Set parameter for a channel mode - special version for SJOIN.
    638  * This version doesn't take a channel, but a mode.mode_params.
    639  * It is only used by SJOIN and should not be used in 3rd party modules.
    640  * @param p	The list, eg oldmode.mode_params
    641  * @param mode	The mode letter
    642  * @param str	The mode parameter string to set
    643  */
    644 void cm_putparameter_ex(void **p, char mode, const char *str)
    645 {
    646 	GETPARASTRUCTEX(p, mode) = GETPARAMHANDLERBYLETTER(mode)->put_param(GETPARASTRUCTEX(p, mode), str);
    647 }
    648 
    649 /** Default handler for - require channel operator or higher (+o/+a/+q)
    650  * @param client	The client issueing the MODE
    651  * @param channel	The channel
    652  * @param mode		The mode letter (eg: 'f')
    653  * @param para		The parameter, if any (can be NULL)
    654  * @param checkt	The check type, one of .....
    655  * @param what		MODE_ADD / MODE_DEL (???)
    656  * @returns EX_ALLOW or EX_DENY
    657  */
    658 int extcmode_default_requirechop(Client *client, Channel *channel, char mode, const char *para, int checkt, int what)
    659 {
    660 	if (IsUser(client) && check_channel_access(client, channel, "oaq"))
    661 		return EX_ALLOW;
    662 	if (checkt == EXCHK_ACCESS_ERR) /* can only be due to being halfop */
    663 		sendnumeric(client, ERR_NOTFORHALFOPS, mode);
    664 	return EX_DENY;
    665 }
    666 
    667 /** Default handler for - require halfop or higher (+h/+o/+a/+q)
    668  * @param client	The client issueing the MODE
    669  * @param channel	The channel
    670  * @param mode		The mode letter (eg: 'f')
    671  * @param para		The parameter, if any (can be NULL)
    672  * @param checkt	The check type, one of .....
    673  * @param what		MODE_ADD / MODE_DEL (???)
    674  * @returns EX_ALLOW or EX_DENY
    675  */
    676 int extcmode_default_requirehalfop(Client *client, Channel *channel, char mode, const char *para, int checkt, int what)
    677 {
    678 	if (IsUser(client) && (check_channel_access(client, channel, "oaq") || check_channel_access(client, channel, "h")))
    679 		return EX_ALLOW;
    680 	return EX_DENY;
    681 }
    682 
    683 /** Duplicate all channel mode parameters - only used by SJOIN.
    684  * @param xi	Input list
    685  * @param xi	Output list
    686  */
    687 void extcmode_duplicate_paramlist(void **xi, void **xo)
    688 {
    689 	int i;
    690 	Cmode *handler;
    691 	void *inx;
    692 
    693 	for (i = 0; i < MAXPARAMMODES; i++)
    694 	{
    695 		handler = CMP_GETHANDLERBYSLOT(i);
    696 		if (!handler)
    697 			continue; /* nothing there.. */
    698 		inx = xi[handler->param_slot]; /* paramter data of input is here */
    699 		if (!inx)
    700 			continue; /* not set */
    701 		xo[handler->param_slot] = handler->dup_struct(inx); /* call dup_struct with that input and set the output param to that */
    702 	}
    703 }
    704 
    705 /** Free all channel mode parameters - only used by SJOIN.
    706  * @param ar	The list
    707  */
    708 void extcmode_free_paramlist(void **ar)
    709 {
    710 	int i;
    711 	Cmode *handler;
    712 
    713 	for (i = 0; i < MAXPARAMMODES; i++)
    714 	{
    715 		handler = GETPARAMHANDLERBYSLOT(i);
    716 		if (!handler)
    717 			continue; /* nothing here... */
    718 		handler->free_param(ar[handler->param_slot], 0);
    719 		ar[handler->param_slot] = NULL;
    720 	}
    721 }
    722 
    723 /** @} */
    724 
    725 /** Internal function: returns 1 if the specified module has 1 or more extended channel modes registered */
    726 int module_has_extcmode_param_mode(Module *mod)
    727 {
    728 	Cmode *cm;
    729 
    730 	for (cm=channelmodes; cm; cm = cm->next)
    731 		if ((cm->letter) && (cm->owner == mod) && (cm->paracount))
    732 			return 1;
    733 
    734 	return 0;
    735 }
    736 
    737 /** Channel member privileges - getting, setting, checking vhoaq status, etc.
    738  * These functions get or set the access rights of channel members, such as +vhoaq.
    739  * They can also convert between modes (vhoaq), prefixes and sjoin prefixes.
    740  * @defgroup ChannelMember Channel members access privileges
    741  * @{
    742  */
    743 
    744 /** Retrieve channel access for a user on a channel, returns modes eg "qao".
    745  * @param client	The client
    746  * @param channel	The channel
    747  * @returns The modes, sorted by high ranking to lower ranking, eg "qao".
    748  * An empty string ("") is returned when not in the channel or no modes.
    749  */
    750 const char *get_channel_access(Client *client, Channel *channel)
    751 {
    752 	Membership *mb;
    753 
    754 	mb = find_membership_link(client->user->channel, channel);
    755 	if (!mb)
    756 		return "";
    757 	return mb->member_modes;
    758 }
    759 
    760 /** Check channel access for user.
    761  * @param client	The client to check
    762  * @param channel	The channel to check
    763  * @param modes		Which mode(s) to check for
    764  * @returns If the client in channel has any of the modes set, 1 is returned.
    765  * Otherwise 0 is returned, which is also the case if the user is
    766  * not a user or is not in the channel at all.
    767  */
    768 int check_channel_access(Client *client, Channel *channel, const char *modes)
    769 {
    770 	Membership *mb;
    771 	const char *p;
    772 
    773 	if (!IsUser(client))
    774 		return 0; /* eg server */
    775 
    776 	mb = find_membership_link(client->user->channel, channel);
    777 	if (!mb)
    778 		return 0; /* not a member */
    779 
    780 	for (p = mb->member_modes; *p; p++)
    781 		if (strchr(modes, *p))
    782 			return 1; /* match new style */
    783 
    784 	return 0; /* nomatch */
    785 }
    786 
    787 /** Check channel access for user.
    788  * @param client	The client to check
    789  * @param channel	The channel to check
    790  * @param modes		Which mode(s) to check for
    791  * @returns If the client in channel has any of the modes set, 1 is returned.
    792  * Otherwise 0 is returned, which is also the case if the user is
    793  * not a user or is not in the channel at all.
    794  */
    795 int check_channel_access_membership(Membership *mb, const char *modes)
    796 {
    797 	const char *p;
    798 
    799 	if (!mb)
    800 		return 0;
    801 
    802 	for (p = mb->member_modes; *p; p++)
    803 		if (strchr(modes, *p))
    804 			return 1; /* match new style */
    805 
    806 	return 0; /* nomatch */
    807 }
    808 
    809 /** Check channel access for user.
    810  * @param client	The client to check
    811  * @param channel	The channel to check
    812  * @param modes		Which mode(s) to check for
    813  * @returns If the client in channel has any of the modes set, 1 is returned.
    814  * Otherwise 0 is returned, which is also the case if the user is
    815  * not a user or is not in the channel at all.
    816  */
    817 int check_channel_access_member(Member *mb, const char *modes)
    818 {
    819 	const char *p;
    820 
    821 	if (!mb)
    822 		return 0;
    823 
    824 	for (p = mb->member_modes; *p; p++)
    825 		if (strchr(modes, *p))
    826 			return 1; /* match new style */
    827 
    828 	return 0; /* nomatch */
    829 }
    830 
    831 /** Check channel access for user.
    832  * @param current	Flags currently set on the client (eg mb->member_modes)
    833  * @param modes		Which mode(s) to check for
    834  * @returns If the client in channel has any of the modes set, 1 is returned.
    835  * Otherwise 0 is returned.
    836  */
    837 int check_channel_access_string(const char *current_modes, const char *modes)
    838 {
    839 	const char *p;
    840 
    841 	for (p = current_modes; *p; p++)
    842 		if (strchr(modes, *p))
    843 			return 1;
    844 
    845 	return 0; /* nomatch */
    846 }
    847 
    848 /** Check channel access for user.
    849  * @param current	Flags currently set on the client (eg mb->member_modes)
    850  * @param letter	Which mode letter to check for
    851  * @returns If the client in channel has any of the modes set, 1 is returned.
    852  * Otherwise 0 is returned.
    853  */
    854 int check_channel_access_letter(const char *current_modes, const char letter)
    855 {
    856 	return strchr(current_modes, letter) ? 1 : 0;
    857 }
    858 
    859 Cmode *find_channel_mode_handler(char letter)
    860 {
    861 	Cmode *cm;
    862 
    863 	for (cm=channelmodes; cm; cm = cm->next)
    864 		if (cm->letter == letter)
    865 			return cm;
    866 	return NULL;
    867 }
    868 
    869 /** Is 'letter' a valid mode used for access/levels/ranks? (vhoaq and such)
    870  * @param letter	The channel mode letter to check, eg 'v'
    871  * @returns 1 if valid, 0 if the channel mode does not exist or is not a level mode.
    872  */
    873 int valid_channel_access_mode_letter(char letter)
    874 {
    875 	Cmode *cm;
    876 
    877 	if ((cm = find_channel_mode_handler(letter)) && (cm->type == CMODE_MEMBER))
    878 		return 1;
    879 
    880 	return 0;
    881 }
    882 
    883 void addlettertomstring(char *str, char letter)
    884 {
    885 	Cmode *cm;
    886 	int n;
    887 	int my_rank;
    888 	char *p;
    889 
    890 	if (!(cm = find_channel_mode_handler(letter)) || (cm->type != CMODE_MEMBER))
    891 		return; // should we BUG on this? if something makes it this far, it can never be good right?
    892 
    893 	my_rank = cm->rank;
    894 
    895 	n = strlen(str);
    896 	if (n >= MEMBERMODESLEN-1)
    897 		return; // panic!
    898 
    899 	for (p = str; *p; p++)
    900 	{
    901 		cm = find_channel_mode_handler(*p);
    902 		if (!cm)
    903 			continue; /* wtf */
    904 		if (cm->rank < my_rank)
    905 		{
    906 			/* We need to insert us here */
    907 			n = strlen(p);
    908 			memmove(p+1, p, n+1); // +1 for NUL byte
    909 			*p = letter;
    910 			return;
    911 		}
    912 	}
    913 	/* We should be at the end */
    914 	str[n] = letter;
    915 	str[n+1] = '\0';
    916 }
    917 
    918 void add_member_mode_fast(Member *mb, Membership *mbs, char letter)
    919 {
    920 	addlettertomstring(mb->member_modes, letter);
    921 	addlettertomstring(mbs->member_modes, letter);
    922 }
    923 
    924 void del_member_mode_fast(Member *mb, Membership *mbs, char letter)
    925 {
    926 	delletterfromstring(mb->member_modes, letter);
    927 	delletterfromstring(mbs->member_modes, letter);
    928 }
    929 
    930 int find_mbs(Client *client, Channel *channel, Member **mb, Membership **mbs)
    931 {
    932 	*mbs = NULL;
    933 
    934 	if (!(*mb = find_member_link(channel->members, client)))
    935 		return 0;
    936 
    937 	if (!(*mbs = find_membership_link(client->user->channel, channel)))
    938 		return 0;
    939 	
    940 	return 1;
    941 }
    942 
    943 void add_member_mode(Client *client, Channel *channel, char letter)
    944 {
    945 	Member *mb;
    946 	Membership *mbs;
    947 
    948 	if (!find_mbs(client, channel, &mb, &mbs))
    949 		return;
    950 
    951 	add_member_mode_fast(mb, mbs, letter);
    952 }
    953 
    954 void del_member_mode(Client *client, Channel *channel, char letter)
    955 {
    956 	Member *mb;
    957 	Membership *mbs;
    958 
    959 	if (!find_mbs(client, channel, &mb, &mbs))
    960 		return;
    961 
    962 	del_member_mode_fast(mb, mbs, letter);
    963 }
    964 
    965 char sjoin_prefix_to_mode(char s)
    966 {
    967 	Cmode *cm;
    968 
    969 	/* Filter this out early to avoid spurious results */
    970 	if (s == '\0')
    971 		return '\0';
    972 
    973 	/* First the hardcoded list modes: */
    974 	if (s == '&')
    975 		return 'b';
    976 	if (s == '"')
    977 		return 'e';
    978 	if (s == '\'')
    979 		return 'I';
    980 
    981 	/* Now the dynamic ones (+vhoaq): */
    982 	for (cm=channelmodes; cm; cm = cm->next)
    983 		if ((cm->sjoin_prefix == s) && (cm->type == CMODE_MEMBER))
    984 			return cm->letter;
    985 
    986 	/* Not found */
    987 	return '\0';
    988 }
    989 
    990 char mode_to_sjoin_prefix(char s)
    991 {
    992 	Cmode *cm;
    993 
    994 	/* Filter this out early to avoid spurious results */
    995 	if (s == '\0')
    996 		return '\0';
    997 
    998 	/* First the hardcoded list modes: */
    999 	if (s == 'b')
   1000 		return '&';
   1001 	if (s == 'e')
   1002 		return '"';
   1003 	if (s == 'I')
   1004 		return '\'';
   1005 
   1006 	/* Now the dynamic ones (+vhoaq): */
   1007 	for (cm=channelmodes; cm; cm = cm->next)
   1008 		if ((cm->letter == s) && (cm->type == CMODE_MEMBER))
   1009 			return cm->sjoin_prefix;
   1010 
   1011 	/* Not found */
   1012 	return '\0';
   1013 }
   1014 
   1015 const char *modes_to_sjoin_prefix(const char *modes)
   1016 {
   1017 	static char buf[MEMBERMODESLEN];
   1018 	const char *m;
   1019 	char f;
   1020 
   1021 	*buf = '\0';
   1022 	for (m = modes; *m; m++)
   1023 	{
   1024 		f = mode_to_sjoin_prefix(*m);
   1025 		if (f)
   1026 			strlcat_letter(buf, f, sizeof(buf));
   1027 	}
   1028 
   1029 	return buf;
   1030 }
   1031 
   1032 char mode_to_prefix(char s)
   1033 {
   1034 	Cmode *cm;
   1035 
   1036 	/* Filter this out early to avoid spurious results */
   1037 	if (s == '\0')
   1038 		return '\0';
   1039 
   1040 	/* Now the dynamic ones (+vhoaq): */
   1041 	for (cm=channelmodes; cm; cm = cm->next)
   1042 		if ((cm->letter == s) && (cm->type == CMODE_MEMBER))
   1043 			return cm->prefix;
   1044 
   1045 	/* Not found */
   1046 	return '\0';
   1047 }
   1048 
   1049 const char *modes_to_prefix(const char *modes)
   1050 {
   1051 	static char buf[MEMBERMODESLEN];
   1052 	const char *m;
   1053 	char f;
   1054 
   1055 	*buf = '\0';
   1056 	for (m = modes; *m; m++)
   1057 	{
   1058 		f = mode_to_prefix(*m);
   1059 		if (f)
   1060 			strlcat_letter(buf, f, sizeof(buf));
   1061 	}
   1062 
   1063 	return buf;
   1064 }
   1065 
   1066 char prefix_to_mode(char s)
   1067 {
   1068 	Cmode *cm;
   1069 
   1070 	/* Filter this out early to avoid spurious results */
   1071 	if (s == '\0')
   1072 		return '\0';
   1073 
   1074 	/* Now the dynamic ones (+vhoaq): */
   1075 	for (cm=channelmodes; cm; cm = cm->next)
   1076 		if ((cm->prefix == s) && (cm->type == CMODE_MEMBER))
   1077 			return cm->letter;
   1078 
   1079 	/* Not found */
   1080 	return '\0';
   1081 }
   1082 
   1083 char rank_to_mode(int rank)
   1084 {
   1085 	Cmode *cm;
   1086 	for (cm=channelmodes; cm; cm = cm->next)
   1087 		if ((cm->type == CMODE_MEMBER) && (cm->rank == rank))
   1088 			return cm->letter;
   1089 	return '\0';
   1090 }
   1091 
   1092 int mode_to_rank(char mode)
   1093 {
   1094 	Cmode *cm;
   1095 	for (cm=channelmodes; cm; cm = cm->next)
   1096 		if ((cm->type == CMODE_MEMBER) && (cm->letter == mode))
   1097 			return cm->rank;
   1098 	return '\0';
   1099 }
   1100 
   1101 int prefix_to_rank(char prefix)
   1102 {
   1103 	Cmode *cm;
   1104 	for (cm=channelmodes; cm; cm = cm->next)
   1105 		if ((cm->type == CMODE_MEMBER) && (cm->prefix == prefix))
   1106 			return cm->rank;
   1107 	return '\0';
   1108 }
   1109 
   1110 char rank_to_prefix(int rank)
   1111 {
   1112 	Cmode *cm;
   1113 	for (cm=channelmodes; cm; cm = cm->next)
   1114 		if ((cm->type == CMODE_MEMBER) && (cm->rank == rank))
   1115 			return cm->prefix;
   1116 	return '\0';
   1117 }
   1118 
   1119 char lowest_ranking_prefix(const char *prefix)
   1120 {
   1121 	const char *p;
   1122 	int winning_rank = INT_MAX;
   1123 
   1124 	for (p = prefix; *p; p++)
   1125 	{
   1126 		int rank = prefix_to_rank(*p);
   1127 		if (rank < winning_rank)
   1128 			winning_rank = rank;
   1129 	}
   1130 	if (winning_rank == INT_MAX)
   1131 		return '\0'; /* No result */
   1132 	return rank_to_prefix(winning_rank);
   1133 }
   1134 
   1135 char lowest_ranking_mode(const char *mode)
   1136 {
   1137 	const char *p;
   1138 	int winning_rank = INT_MAX;
   1139 
   1140 	for (p = mode; *p; p++)
   1141 	{
   1142 		int rank = mode_to_rank(*p);
   1143 		if (rank < winning_rank)
   1144 			winning_rank = rank;
   1145 	}
   1146 	if (winning_rank == INT_MAX)
   1147 		return '\0'; /* No result */
   1148 	return rank_to_mode(winning_rank);
   1149 }
   1150 
   1151 /** Generate all member modes that are equal or greater than 'modes'.
   1152  * Eg calling this with "o" would generate "oaq" with the default loaded modules.
   1153  * This is used in sendto_channel() to make multiple check_channel_access_member()
   1154  * calls more easy / faster.
   1155  */
   1156 void channel_member_modes_generate_equal_or_greater(const char *modes, char *buf, size_t buflen)
   1157 {
   1158 	const char *p;
   1159 	int rank;
   1160 	Cmode *cm;
   1161 
   1162 	*buf = '\0';
   1163 
   1164 	/* First we must grab the lowest ranking mode, eg 'vhoaq' results in rank for 'v' */
   1165 	rank = lowest_ranking_mode(modes);
   1166 	if (!rank)
   1167 		return; /* zero matches */
   1168 
   1169 	for (cm=channelmodes; cm; cm = cm->next)
   1170 	if ((cm->type == CMODE_MEMBER) && (cm->rank >= rank))
   1171 		strlcat_letter(buf, cm->letter, buflen);
   1172 }
   1173 
   1174 /** @} */