unrealircd

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

message.c (19072B)

      1 /*
      2  *   Unreal Internet Relay Chat Daemon, src/modules/message.c
      3  *   (C) 2000-2001 Carsten V. Munk and the UnrealIRCd Team
      4  *   Moved to modules by Fish (Justin Hammond)
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation; either version 1, or (at your option)
      9  *   any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU General Public License
     17  *   along with this program; if not, write to the Free Software
     18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 #include "unrealircd.h"
     22 
     23 /* Forward declarations */
     24 const char *_StripColors(const char *text);
     25 int ban_version(Client *client, const char *text);
     26 CMD_FUNC(cmd_private);
     27 CMD_FUNC(cmd_notice);
     28 CMD_FUNC(cmd_tagmsg);
     29 void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype);
     30 int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype);
     31 int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype);
     32 
     33 /* Variables */
     34 long CAP_MESSAGE_TAGS = 0; /**< Looked up at MOD_LOAD, may stay 0 if message-tags support is absent */
     35 
     36 ModuleHeader MOD_HEADER
     37   = {
     38 	"message",	/* Name of module */
     39 	"6.0.2", /* Version */
     40 	"private message and notice", /* Short description of module */
     41 	"UnrealIRCd Team",
     42 	"unrealircd-6",
     43     };
     44 
     45 MOD_TEST()
     46 {
     47 	MARK_AS_OFFICIAL_MODULE(modinfo);
     48 	EfunctionAddConstString(modinfo->handle, EFUNC_STRIPCOLORS, _StripColors);
     49 	EfunctionAdd(modinfo->handle, EFUNC_CAN_SEND_TO_CHANNEL, _can_send_to_channel);
     50 	return MOD_SUCCESS;
     51 }
     52 
     53 /* This is called on module init, before Server Ready */
     54 MOD_INIT()
     55 {
     56 	CommandAdd(modinfo->handle, "PRIVMSG", cmd_private, 2, CMD_USER|CMD_SERVER|CMD_RESETIDLE|CMD_VIRUS);
     57 	CommandAdd(modinfo->handle, "NOTICE", cmd_notice, 2, CMD_USER|CMD_SERVER);
     58 	CommandAdd(modinfo->handle, "TAGMSG", cmd_tagmsg, 1, CMD_USER|CMD_SERVER);
     59 	MARK_AS_OFFICIAL_MODULE(modinfo);
     60 	return MOD_SUCCESS;
     61 }
     62 
     63 /* Is first run when server is 100% ready */
     64 MOD_LOAD()
     65 {
     66 	CAP_MESSAGE_TAGS = ClientCapabilityBit("message-tags");
     67 
     68 	return MOD_SUCCESS;
     69 }
     70 
     71 /* Called when module is unloaded */
     72 MOD_UNLOAD()
     73 {
     74 	return MOD_SUCCESS;
     75 }
     76 
     77 #define CANPRIVMSG_CONTINUE		100
     78 #define CANPRIVMSG_SEND			101
     79 /** Check if PRIVMSG's are permitted from a person to another person.
     80  * client:	source client
     81  * target:	target client
     82  * sendtype:	One of SEND_TYPE_*
     83  * text:	Pointer to a pointer to a text [in, out]
     84  * cmd:		Pointer to a pointer which contains the command to use [in, out]
     85  */
     86 int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype)
     87 {
     88 	int ret;
     89 	Hook *h;
     90 	int n;
     91 	static char errbuf[256];
     92 
     93 	*errmsg = NULL;
     94 
     95 	if (IsVirus(client))
     96 	{
     97 		ircsnprintf(errbuf, sizeof(errbuf), "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN);
     98 		*errmsg = errbuf;
     99 		return 0;
    100 	}
    101 
    102 	if (MyUser(client) && target_limit_exceeded(client, target, target->name))
    103 	{
    104 		/* target_limit_exceeded() is an exception, in the sense that
    105 		 * it will send a different numeric. So we don't set errmsg.
    106 		 */
    107 		return 0;
    108 	}
    109 
    110 	if (is_silenced(client, target))
    111 	{
    112 		RunHook(HOOKTYPE_SILENCED, client, target, sendtype);
    113 		/* Silently discarded, no error message */
    114 		return 0;
    115 	}
    116 
    117 	// Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric?
    118 	if (MyUser(client))
    119 	{
    120 		int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG);
    121 		const char *cmd = sendtype_to_cmd(sendtype);
    122 
    123 		if (match_spamfilter(client, *msgtext, spamtype, cmd, target->name, 0, NULL))
    124 			return 0;
    125 	}
    126 
    127 	n = HOOK_CONTINUE;
    128 	for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next)
    129 	{
    130 		n = (*(h->func.intfunc))(client, target, msgtext, errmsg, sendtype);
    131 		if (n == HOOK_DENY)
    132 		{
    133 			if (!*errmsg)
    134 			{
    135 				unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_USER_NO_ERRMSG", client,
    136 					   "[BUG] Module $module did not set errmsg!!!",
    137 					   log_data_string("module", h->owner->header->name));
    138 				abort();
    139 			}
    140 			return 0;
    141 		}
    142 		if (!*msgtext || !**msgtext)
    143 		{
    144 			if (sendtype != SEND_TYPE_TAGMSG)
    145 				return 0;
    146 			else
    147 				*msgtext = "";
    148 		}
    149 	}
    150 
    151 	return 1;
    152 }
    153 
    154 /** Check if user is allowed to send to a prefix (eg: @#channel).
    155  * @param client	The client (sender)
    156  * @param channel	The target channel
    157  * @param mode		The member mode to send to (eg: 'o')
    158  */
    159 int can_send_to_member_mode(Client *client, Channel *channel, char mode)
    160 {
    161 	Membership *lp;
    162 
    163 	if (op_can_override("channel:override:message:prefix",client,channel,NULL))
    164 		return 1;
    165 
    166 	lp = find_membership_link(client->user->channel, channel);
    167 
    168 	/* Check if user is allowed to send. RULES:
    169 	 * Need at least voice (+) in order to send to +,% or @
    170 	 * Need at least ops (@) in order to send to & or ~
    171 	 */
    172 	if (!lp || !check_channel_access_membership(lp, "vhoaq"))
    173 	{
    174 		sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name);
    175 		return 0;
    176 	}
    177 
    178 #if 0
    179 	if (!(prefix & PREFIX_OP) && ((prefix & PREFIX_OWNER) || (prefix & PREFIX_ADMIN)) &&
    180 	    !check_channel_access_membership(lp, "oaq"))
    181 	{
    182 		sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name);
    183 		return 0;
    184 	}
    185 #endif
    186 
    187 	return 1;
    188 }
    189 
    190 int has_client_mtags(MessageTag *mtags)
    191 {
    192 	MessageTag *m;
    193 
    194 	for (m = mtags; m; m = m->next)
    195 		if (*m->name == '+')
    196 			return 1;
    197 	return 0;
    198 }
    199 
    200 /* General message handler to users and channels. Used by PRIVMSG, NOTICE, etc.
    201  */
    202 void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype)
    203 {
    204 	Client *target;
    205 	Channel *channel;
    206 	char targets[BUFSIZE];
    207 	char *targetstr, *p, *p2, *pc;
    208 	const char *text, *errmsg;
    209 	int ret;
    210 	int ntargets = 0;
    211 	const char *cmd = sendtype_to_cmd(sendtype);
    212 	int maxtargets = max_targets_for_command(cmd);
    213 	Hook *h;
    214 	MessageTag *mtags;
    215 	int sendflags;
    216 
    217 	/* Force a labeled-response, even if we don't send anything
    218 	 * and the request was sent to other servers (which won't
    219 	 * reply either :D).
    220 	 */
    221 	labeled_response_force = 1;
    222 
    223 	if (parc < 2 || *parv[1] == '\0')
    224 	{
    225 		sendnumeric(client, ERR_NORECIPIENT, cmd);
    226 		return;
    227 	}
    228 
    229 	if ((sendtype != SEND_TYPE_TAGMSG) && (parc < 3 || *parv[2] == '\0'))
    230 	{
    231 		sendnumeric(client, ERR_NOTEXTTOSEND);
    232 		return;
    233 	}
    234 
    235 	if (MyConnect(client))
    236 		parv[1] = (char *)canonize(parv[1]);
    237 
    238 	strlcpy(targets, parv[1], sizeof(targets));
    239 	for (p = NULL, targetstr = strtoken(&p, targets, ","); targetstr; targetstr = strtoken(&p, NULL, ","))
    240 	{
    241 		if (MyUser(client) && (++ntargets > maxtargets))
    242 		{
    243 			sendnumeric(client, ERR_TOOMANYTARGETS, targetstr, maxtargets, cmd);
    244 			break;
    245 		}
    246 
    247 		/* The nicks "ircd" and "irc" are special (and reserved) */
    248 		if (!strcasecmp(targetstr, "ircd") && MyUser(client))
    249 			return;
    250 
    251 		if (!strcasecmp(targetstr, "irc") && MyUser(client))
    252 		{
    253 			/* When ban version { } is enabled the IRCd sends a CTCP VERSION request
    254 			 * from the "IRC" nick. So we need to handle CTCP VERSION replies to "IRC".
    255 			 */
    256 			if (!strncmp(parv[2], "\1VERSION ", 9))
    257 				ban_version(client, parv[2] + 9);
    258 			else if (!strncmp(parv[2], "\1SCRIPT ", 8))
    259 				ban_version(client, parv[2] + 8);
    260 			return;
    261 		}
    262 
    263 		p2 = strchr(targetstr, '#');
    264 
    265 		/* Message to channel */
    266 		if (p2 && (channel = find_channel(p2)))
    267 		{
    268 			char pfixchan[CHANNELLEN + 4];
    269 			int replaced = 0;
    270 			char member_modes_tmp[2];
    271 			char *member_modes = NULL;
    272 			if (p2 - targetstr > 0)
    273 			{
    274 				/* There is (posssibly) a prefix involved... */
    275 				char prefix_tmp[32];
    276 				char prefix;
    277 				strlncpy(prefix_tmp, targetstr, sizeof(prefix_tmp), p2 - targetstr);
    278 				prefix = lowest_ranking_prefix(prefix_tmp);
    279 				if (prefix)
    280 				{
    281 					/* Rewrite the target. Eg: @&~#chan becomes @#chan */
    282 					snprintf(pfixchan, sizeof(pfixchan), "%c%s", prefix, channel->name);
    283 					targetstr = pfixchan;
    284 					replaced = 1;
    285 					/* And set 'member_modes' */
    286 					member_modes_tmp[0] = prefix_to_mode(prefix);
    287 					member_modes_tmp[1] = '\0';
    288 					member_modes = member_modes_tmp;
    289 					/* Oh, and some access check */
    290 					if (MyUser(client) && !can_send_to_member_mode(client, channel, *member_modes))
    291 						continue;
    292 				}
    293 			}
    294 			if (!replaced)
    295 			{
    296 				/* Replace target so the privmsg always goes to the "official" channel name */
    297 				strlcpy(pfixchan, channel->name, sizeof(pfixchan));
    298 				targetstr = pfixchan;
    299 			}
    300 
    301 			if (IsVirus(client) && strcasecmp(channel->name, SPAMFILTER_VIRUSCHAN))
    302 			{
    303 				sendnotice(client, "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN);
    304 				continue;
    305 			}
    306 
    307 			text = parv[2];
    308 			errmsg = NULL;
    309 			if (MyUser(client) && !IsULine(client))
    310 			{
    311 				if (!can_send_to_channel(client, channel, &text, &errmsg, sendtype))
    312 				{
    313 					/* Send the error message, but only if:
    314 					 * 1) The user has not been killed
    315 					 * 2) It is not a NOTICE
    316 					 */
    317 					if (IsDead(client))
    318 						return;
    319 					if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg))
    320 						sendnumeric(client, ERR_CANNOTSENDTOCHAN, channel->name, errmsg, p2);
    321 					continue; /* skip delivery to this target */
    322 				}
    323 			}
    324 			mtags = NULL;
    325 			sendflags = SEND_ALL;
    326 
    327 			if (!strchr(CHANCMDPFX,parv[2][0]))
    328 				sendflags |= SKIP_DEAF;
    329 
    330 			if ((*parv[2] == '\001') && strncmp(&parv[2][1], "ACTION ", 7))
    331 				sendflags |= SKIP_CTCP;
    332 
    333 			if (MyUser(client))
    334 			{
    335 				int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG);
    336 
    337 				if (match_spamfilter(client, text, spamtype, cmd, channel->name, 0, NULL))
    338 					return;
    339 			}
    340 
    341 			new_message(client, recv_mtags, &mtags);
    342 
    343 			RunHook(HOOKTYPE_PRE_CHANMSG, client, channel, mtags, text, sendtype);
    344 
    345 			if (!text)
    346 			{
    347 				free_message_tags(mtags);
    348 				continue;
    349 			}
    350 
    351 			if (sendtype != SEND_TYPE_TAGMSG)
    352 			{
    353 				/* PRIVMSG or NOTICE */
    354 				sendto_channel(channel, client, client->direction,
    355 					       member_modes, 0, sendflags, mtags,
    356 					       ":%s %s %s :%s",
    357 					       client->name, cmd, targetstr, text);
    358 			} else {
    359 				/* TAGMSG:
    360 				 * Only send if the message includes any user message tags
    361 				 * and if the 'message-tags' module is loaded.
    362 				 * Do not allow empty and useless TAGMSG.
    363 				 */
    364 				if (!CAP_MESSAGE_TAGS || !has_client_mtags(mtags))
    365 				{
    366 					free_message_tags(mtags);
    367 					continue;
    368 				}
    369 				sendto_channel(channel, client, client->direction,
    370 					       member_modes, CAP_MESSAGE_TAGS, sendflags, mtags,
    371 					       ":%s TAGMSG %s",
    372 					       client->name, targetstr);
    373 			}
    374 
    375 			RunHook(HOOKTYPE_CHANMSG, client, channel, sendflags, member_modes, targetstr, mtags, text, sendtype);
    376 
    377 			free_message_tags(mtags);
    378 
    379 			continue;
    380 		}
    381 		else if (p2)
    382 		{
    383 			sendnumeric(client, ERR_NOSUCHNICK, p2);
    384 			continue;
    385 		}
    386 
    387 
    388 		/* Message to $servermask */
    389 		if (*targetstr == '$')
    390 		{
    391 			MessageTag *mtags = NULL;
    392 
    393 			if (!ValidatePermissionsForPath("chat:notice:global", client, NULL, NULL, NULL))
    394 			{
    395 				/* Apparently no other IRCd does this, but I think it's confusing not to
    396 				 * send an error message, especially with our new privilege system.
    397 				 * Error message could be more descriptive perhaps.
    398 				 */
    399 				sendnumeric(client, ERR_NOPRIVILEGES);
    400 				continue;
    401 			}
    402 			new_message(client, recv_mtags, &mtags);
    403 			sendto_match_butone(IsServer(client->direction) ? client->direction : NULL,
    404 			    client, targetstr + 1,
    405 			    (*targetstr == '#') ? MATCH_HOST :
    406 			    MATCH_SERVER,
    407 			    mtags,
    408 			    ":%s %s %s :%s", client->name, cmd, targetstr, parv[2]);
    409 			free_message_tags(mtags);
    410 			continue;
    411 		}
    412 
    413 		/* nickname addressed? */
    414 		target = hash_find_nickatserver(targetstr, NULL);
    415 		if (target)
    416 		{
    417 			const char *errmsg = NULL;
    418 			text = parv[2];
    419 			if (!can_send_to_user(client, target, &text, &errmsg, sendtype))
    420 			{
    421 				/* Message is discarded */
    422 				if (IsDead(client))
    423 					return;
    424 				if ((sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg))
    425 					sendnumeric(client, ERR_CANTSENDTOUSER, target->name, errmsg);
    426 			} else
    427 			{
    428 				/* We may send the message */
    429 				MessageTag *mtags = NULL;
    430 
    431 				/* Inform sender that recipient is away, if this is so */
    432 				if ((sendtype == SEND_TYPE_PRIVMSG) && MyConnect(client) && target->user && target->user->away)
    433 					sendnumeric(client, RPL_AWAY, target->name, target->user->away);
    434 
    435 				new_message(client, recv_mtags, &mtags);
    436 				if ((sendtype == SEND_TYPE_TAGMSG) && !has_client_mtags(mtags))
    437 				{
    438 					free_message_tags(mtags);
    439 					continue;
    440 				}
    441 				labeled_response_inhibit = 1;
    442 				if (MyUser(target))
    443 				{
    444 					/* Deliver to end-user */
    445 					if (sendtype == SEND_TYPE_TAGMSG)
    446 					{
    447 						if (HasCapability(target, "message-tags"))
    448 						{
    449 							sendto_prefix_one(target, client, mtags, ":%s %s %s",
    450 									  client->name, cmd, target->name);
    451 						}
    452 					} else {
    453 						sendto_prefix_one(target, client, mtags, ":%s %s %s :%s",
    454 								  client->name, cmd, target->name, text);
    455 					}
    456 				} else {
    457 					/* Send to another server */
    458 					if (sendtype == SEND_TYPE_TAGMSG)
    459 					{
    460 						sendto_prefix_one(target, client, mtags, ":%s %s %s",
    461 								  client->id, cmd, target->id);
    462 					} else {
    463 						sendto_prefix_one(target, client, mtags, ":%s %s %s :%s",
    464 								  client->id, cmd, target->id, text);
    465 					}
    466 				}
    467 				labeled_response_inhibit = 0;
    468 				RunHook(HOOKTYPE_USERMSG, client, target, mtags, text, sendtype);
    469 				free_message_tags(mtags);
    470 				continue;
    471 			}
    472 			continue; /* Message has been delivered or rejected, continue with next target */
    473 		}
    474 
    475 		/* If nick@server -and- the @server portion was set::services-server then send a special message */
    476 		if (!target && SERVICES_NAME)
    477 		{
    478 			char *server = strchr(targetstr, '@');
    479 			if (server && strncasecmp(server + 1, SERVICES_NAME, strlen(SERVICES_NAME)) == 0)
    480 			{
    481 				sendnumeric(client, ERR_SERVICESDOWN, targetstr);
    482 				continue;
    483 			}
    484 		}
    485 
    486 		/* nothing, nada, not anything found */
    487 		sendnumeric(client, ERR_NOSUCHNICK, targetstr);
    488 		continue;
    489 	}
    490 }
    491 
    492 /*
    493 ** cmd_private
    494 **	parv[1] = receiver list
    495 **	parv[2] = message text
    496 */
    497 CMD_FUNC(cmd_private)
    498 {
    499 	cmd_message(client, recv_mtags, parc, parv, SEND_TYPE_PRIVMSG);
    500 }
    501 
    502 /*
    503 ** cmd_notice
    504 **	parv[1] = receiver list
    505 **	parv[2] = notice text
    506 */
    507 CMD_FUNC(cmd_notice)
    508 {
    509 	cmd_message(client, recv_mtags, parc, parv, SEND_TYPE_NOTICE);
    510 }
    511 
    512 /*
    513 ** cmd_tagmsg
    514 **	parv[1] = receiver list
    515 */
    516 CMD_FUNC(cmd_tagmsg)
    517 {
    518 	/* compatibility hack */
    519 	parv[2] = "";
    520 	parv[3] = NULL;
    521 	cmd_message(client, recv_mtags, parc, parv, SEND_TYPE_TAGMSG);
    522 }
    523 
    524 /* Taken from xchat by Peter Zelezny
    525  * changed very slightly by codemastr
    526  * RGB color stripping support added -- codemastr
    527  *
    528  * NOTE: if you change/update/enhance StripColors() then consider changing
    529  *       the StripControlCodes() function as well (in misc.c) !!
    530  */
    531 const char *_StripColors(const char *text)
    532 {
    533 	int i = 0, len = strlen(text), save_len=0;
    534 	char nc = 0, col = 0, rgb = 0;
    535 	const char *save_text=NULL;
    536 	static char new_str[4096];
    537 
    538 	while (len > 0) 
    539 	{
    540 		if ((col && isdigit(*text) && nc < 2) ||
    541 		    ((col == 1) && (*text == ',') && isdigit(text[1]) && (nc > 0) && (nc < 3)))
    542 		{
    543 			nc++;
    544 			if (*text == ',')
    545 			{
    546 				nc = 0;
    547 				col++;
    548 			}
    549 		}
    550 		/* Syntax for RGB is ^DHHHHHH where H is a hex digit.
    551 		 * If < 6 hex digits are specified, the code is displayed
    552 		 * as text
    553 		 */
    554 		else if ((rgb && isxdigit(*text) && nc < 6) || (rgb && *text == ',' && nc < 7))
    555 		{
    556 			nc++;
    557 			if (*text == ',')
    558 				nc = 0;
    559 		}
    560 		else 
    561 		{
    562 			if (col)
    563 				col = 0;
    564 			if (rgb)
    565 			{
    566 				if (nc != 6)
    567 				{
    568 					text = save_text+1;
    569 					len = save_len-1;
    570 					rgb = 0;
    571 					continue;
    572 				}
    573 				rgb = 0;
    574 			}
    575 			if (*text == '\003') 
    576 			{
    577 				col = 1;
    578 				nc = 0;
    579 			}
    580 			else if (*text == '\004')
    581 			{
    582 				save_text = text;
    583 				save_len = len;
    584 				rgb = 1;
    585 				nc = 0;
    586 			}
    587 			else if (*text != '\026') /* (strip reverse too) */
    588 			{
    589 				new_str[i] = *text;
    590 				i++;
    591 			}
    592 		}
    593 		text++;
    594 		len--;
    595 	}
    596 	new_str[i] = 0;
    597 	if (new_str[0] == '\0')
    598 		return NULL;
    599 	return new_str;
    600 }
    601 
    602 /** Check ban version { } blocks, returns 1  if banned and  0 if not. */
    603 int ban_version(Client *client, const char *text)
    604 {
    605 	int len;
    606 	ConfigItem_ban *ban;
    607 	char ctcp_reply[BUFSIZE];
    608 
    609 	strlcpy(ctcp_reply, text, sizeof(ctcp_reply));
    610 	len = strlen(ctcp_reply);
    611 	if (!len)
    612 		return 0;
    613 	
    614 	if (ctcp_reply[len-1] == '\1')
    615 		ctcp_reply[len-1] = '\0'; /* remove CTCP REPLY terminator (ASCII 1) */
    616 
    617 	if ((ban = find_ban(NULL, ctcp_reply, CONF_BAN_VERSION)))
    618 	{
    619 		if (IsSoftBanAction(ban->action) && IsLoggedIn(client))
    620 			return 0; /* soft ban does not apply to us, we are logged in */
    621 
    622 		if (find_tkl_exception(TKL_BAN_VERSION, client))
    623 			return 0; /* we are exempt */
    624 
    625 		place_host_ban(client, ban->action, ban->reason, BAN_VERSION_TKL_TIME);
    626 		return 1;
    627 	}
    628 
    629 	return 0;
    630 }
    631 
    632 /** Can user send a message to this channel?
    633  * @param client    The client
    634  * @param channel   The channel
    635  * @param msgtext   The message to send (MAY be changed, even if user is allowed to send)
    636  * @param errmsg    The error message (will be filled in)
    637  * @param sendtype  One of SEND_TYPE_*
    638  * @returns Returns 1 if the user is allowed to send, otherwise 0.
    639  * (note that this behavior was reversed in UnrealIRCd versions <5.x.
    640  */
    641 int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype)
    642 {
    643 	Membership *lp;
    644 	int  member, i = 0;
    645 	Hook *h;
    646 
    647 	if (!MyUser(client))
    648 		return 1;
    649 
    650 	*errmsg = NULL;
    651 
    652 	member = IsMember(client, channel);
    653 
    654 	lp = find_membership_link(client->user->channel, channel);
    655 
    656 	/* Modules can plug in as well */
    657 	for (h = Hooks[HOOKTYPE_CAN_SEND_TO_CHANNEL]; h; h = h->next)
    658 	{
    659 		i = (*(h->func.intfunc))(client, channel, lp, msgtext, errmsg, sendtype);
    660 		if (i != HOOK_CONTINUE)
    661 		{
    662 			if (!*errmsg)
    663 			{
    664 				unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_CHANNEL_NO_ERRMSG", client,
    665 					   "[BUG] Module $module did not set errmsg!!!",
    666 					   log_data_string("module", h->owner->header->name));
    667 				abort();
    668 			}
    669 			break;
    670 		}
    671 		if (!*msgtext || !**msgtext)
    672 		{
    673 			if (sendtype != SEND_TYPE_TAGMSG)
    674 				return 0;
    675 			else
    676 				*msgtext = "";
    677 		}
    678 	}
    679 
    680 	if (i != HOOK_CONTINUE)
    681 	{
    682 		if (!*errmsg)
    683 			*errmsg = "You are banned";
    684 		/* Don't send message if the user was previously a member
    685 		 * and isn't anymore, so if the user is KICK'ed, eg by floodprot.
    686 		 */
    687 		if (member && !IsDead(client) && !find_membership_link(client->user->channel, channel))
    688 			*errmsg = NULL;
    689 		return 0;
    690 	}
    691 
    692 	/* Now we are going to check bans */
    693 
    694 	/* ..but first: exempt ircops */
    695 	if (op_can_override("channel:override:message:ban",client,channel,NULL))
    696 		return 1;
    697 
    698 	/* If local client is banned and not +vhoaq... */
    699 	if (MyUser(client) &&
    700 	    !check_channel_access_membership(lp, "vhoaq") &&
    701 	    is_banned(client, channel, BANCHK_MSG, msgtext, errmsg))
    702 	{
    703 		/* Modules can set 'errmsg', otherwise we default to this: */
    704 		if (!*errmsg)
    705 			*errmsg = "You are banned";
    706 		return 0;
    707 	}
    708 
    709 	return 1;
    710 }