unrealircd

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

kick.c (11375B)

      1 /*
      2  *   IRC - Internet Relay Chat, src/modules/kick.c
      3  *   (C) 2004 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 #include "unrealircd.h"
     24 
     25 ModuleHeader MOD_HEADER
     26   = {
     27 	"kick",
     28 	"5.0",
     29 	"command /kick",
     30 	"UnrealIRCd Team",
     31 	"unrealircd-6",
     32     };
     33 
     34 /* Forward declarations */
     35 CMD_FUNC(cmd_kick);
     36 void _kick_user(MessageTag *mtags, Channel *channel, Client *client, Client *victim, char *comment);
     37 
     38 MOD_TEST()
     39 {
     40 	MARK_AS_OFFICIAL_MODULE(modinfo);
     41 	EfunctionAddVoid(modinfo->handle, EFUNC_KICK_USER, _kick_user);
     42 	return MOD_SUCCESS;
     43 }
     44 
     45 MOD_INIT()
     46 {
     47 	CommandAdd(modinfo->handle, "KICK", cmd_kick, 3, CMD_USER|CMD_SERVER);
     48 	MARK_AS_OFFICIAL_MODULE(modinfo);
     49 	return MOD_SUCCESS;
     50 }
     51 
     52 MOD_LOAD()
     53 {
     54 	return MOD_SUCCESS;
     55 }
     56 
     57 MOD_UNLOAD()
     58 {
     59 	return MOD_SUCCESS;
     60 }
     61 
     62 void kick_operoverride_msg(Client *client, Channel *channel, Client *target, char *reason)
     63 {
     64 	unreal_log(ULOG_INFO, "operoverride", "OPEROVERRIDE_KICK", client,
     65 		   "OperOverride: $client.details kicked $target from $channel ($reason)",
     66 		   log_data_string("override_type", "kick"),
     67 		   log_data_string("reason", reason),
     68 		   log_data_client("target", target),
     69 		   log_data_channel("channel", channel));
     70 }
     71 
     72 /** Kick a user from a channel.
     73  * @param initial_mtags	Message tags associated with this KICK (can be NULL)
     74  * @param channel	The channel where the KICK should happen
     75  * @param client	The evil user doing the kick, can be &me
     76  * @param victim	The target user that will be kicked
     77  * @param comment	The KICK comment (cannot be NULL)
     78  * @notes The msgid in initial_mtags is actually used as a prefix.
     79  *        The actual mtag will be "initial_mtags_msgid-suffix_msgid"
     80  *        All this is done in order for message tags to be
     81  *        consistent accross servers.
     82  *        The suffix is necessary to handle multi-target-kicks.
     83  *        If initial_mtags is NULL then we will autogenerate one.
     84  */
     85 void _kick_user(MessageTag *initial_mtags, Channel *channel, Client *client, Client *victim, char *comment)
     86 {
     87 	MessageTag *mtags = NULL;
     88 	int initial_mtags_generated = 0;
     89 
     90 	if (!initial_mtags)
     91 	{
     92 		/* Yeah, we allow callers to be lazy.. */
     93 		initial_mtags_generated = 1;
     94 		new_message(client, NULL, &initial_mtags);
     95 	}
     96 
     97 	new_message_special(client, initial_mtags, &mtags, ":%s KICK %s %s", client->name, channel->name, victim->name);
     98 	/* The same message is actually sent at 5 places below (though max 4 at most) */
     99 
    100 	if (MyUser(client))
    101 		RunHook(HOOKTYPE_LOCAL_KICK, client, victim, channel, mtags, comment);
    102 	else
    103 		RunHook(HOOKTYPE_REMOTE_KICK, client, victim, channel, mtags, comment);
    104 
    105 	if (invisible_user_in_channel(victim, channel))
    106 	{
    107 		/* Send it only to chanops & victim */
    108 		sendto_channel(channel, client, victim,
    109 			       "h", 0,
    110 			       SEND_LOCAL, mtags,
    111 			       ":%s KICK %s %s :%s",
    112 			       client->name, channel->name, victim->name, comment);
    113 
    114 		if (MyUser(victim))
    115 		{
    116 			sendto_prefix_one(victim, client, mtags, ":%s KICK %s %s :%s",
    117 				client->name, channel->name, victim->name, comment);
    118 		}
    119 	} else {
    120 		/* NORMAL */
    121 		sendto_channel(channel, client, NULL, 0, 0, SEND_LOCAL, mtags,
    122 			       ":%s KICK %s %s :%s",
    123 			       client->name, channel->name, victim->name, comment);
    124 	}
    125 
    126 	sendto_server(client, 0, 0, mtags, ":%s KICK %s %s :%s",
    127 	    client->id, channel->name, victim->id, comment);
    128 
    129 	free_message_tags(mtags);
    130 	if (initial_mtags_generated)
    131 	{
    132 		free_message_tags(initial_mtags);
    133 		initial_mtags = NULL;
    134 	}
    135 
    136 	if (MyUser(victim))
    137 	{
    138 		unreal_log(ULOG_INFO, "kick", "LOCAL_CLIENT_KICK", victim,
    139 		           "User $client kicked from $channel",
    140 		           log_data_channel("channel", channel));
    141 	} else {
    142 		unreal_log(ULOG_INFO, "kick", "REMOTE_CLIENT_KICK", victim,
    143 		           "User $client kicked from $channel",
    144 		           log_data_channel("channel", channel));
    145 	}
    146 
    147 	remove_user_from_channel(victim, channel, 1);
    148 }
    149 
    150 /*
    151 ** cmd_kick
    152 **	parv[1] = channel (single channel)
    153 **	parv[2] = client to kick (comma separated)
    154 **	parv[3] = kick comment
    155 */
    156 
    157 CMD_FUNC(cmd_kick)
    158 {
    159 	Client *target;
    160 	Channel *channel;
    161 	int  chasing = 0;
    162 	char *p = NULL, *user, *p2 = NULL, *badkick;
    163 	char comment[MAXKICKLEN+1];
    164 	Membership *lp;
    165 	Hook *h;
    166 	int ret;
    167 	int ntargets = 0;
    168 	int maxtargets = max_targets_for_command("KICK");
    169 	MessageTag *mtags;
    170 	char request[BUFSIZE];
    171 	char request_chans[BUFSIZE];
    172 	const char *client_member_modes = NULL;
    173 	const char *target_member_modes;
    174 
    175 	if (parc < 3 || *parv[1] == '\0')
    176 	{
    177 		sendnumeric(client, ERR_NEEDMOREPARAMS, "KICK");
    178 		return;
    179 	}
    180 
    181 	if (BadPtr(parv[3]))
    182 		strlcpy(comment, client->name, sizeof(comment));
    183 	else
    184 		strlncpy(comment, parv[3], sizeof(comment), iConf.kick_length);
    185 
    186 	strlcpy(request_chans, parv[1], sizeof(request_chans));
    187 	p = strchr(request_chans, ',');
    188 	if (p)
    189 		*p = '\0';
    190 	channel = find_channel(request_chans);
    191 	if (!channel)
    192 	{
    193 		sendnumeric(client, ERR_NOSUCHCHANNEL, request_chans);
    194 		return;
    195 	}
    196 
    197 	/* Store "client" access flags */
    198 	if (IsUser(client))
    199 		client_member_modes = get_channel_access(client, channel);
    200 	if (MyUser(client) && !IsULine(client) &&
    201 	    !op_can_override("channel:override:kick:no-ops",client,channel,NULL) &&
    202 	    !check_channel_access(client, channel, "hoaq"))
    203 	{
    204 		sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name);
    205 		return;
    206 	}
    207 
    208 	strlcpy(request, parv[2], sizeof(request));
    209 	for (user = strtoken(&p2, request, ","); user; user = strtoken(&p2, NULL, ","))
    210 	{
    211 		if (MyUser(client) && (++ntargets > maxtargets))
    212 		{
    213 			sendnumeric(client, ERR_TOOMANYTARGETS, user, maxtargets, "KICK");
    214 			break;
    215 		}
    216 
    217 		if (!(target = find_chasing(client, user, &chasing)))
    218 			continue;	/* No such user left! */
    219 
    220 		if (!target->user)
    221 			continue; /* non-user */
    222 
    223 		lp = find_membership_link(target->user->channel, channel);
    224 		if (!lp)
    225 		{
    226 			if (MyUser(client))
    227 				sendnumeric(client, ERR_USERNOTINCHANNEL, user, request_chans);
    228 			continue;
    229 		}
    230 
    231 		if (IsULine(client) || IsServer(client) || IsMe(client))
    232 			goto attack;
    233 
    234 		/* Note for coders regarding oper override:
    235 		 * always let a remote kick (=from a user on another server) through or
    236 		 * else we will get desynced. In short this means all the denying should
    237 		 * always contain a && MyUser(client) and at the end
    238 		 * a remote kick should always be allowed (pass through). -- Syzop
    239 		 */
    240 
    241 		/* Store "target" access flags */
    242 		target_member_modes = get_channel_access(target, channel);
    243 
    244 		badkick = NULL;
    245 		ret = EX_ALLOW;
    246 		for (h = Hooks[HOOKTYPE_CAN_KICK]; h; h = h->next) {
    247 			int n = (*(h->func.intfunc))(client, target, channel, comment, client_member_modes, target_member_modes, &badkick);
    248 
    249 			if (n == EX_DENY)
    250 				ret = n;
    251 			else if (n == EX_ALWAYS_DENY)
    252 			{
    253 				ret = n;
    254 				break;
    255 			}
    256 		}
    257 
    258 		if (ret == EX_ALWAYS_DENY)
    259 		{
    260 			if (MyUser(client) && badkick)
    261 				sendto_one(client, NULL, "%s", badkick); /* send error, if any */
    262 
    263 			if (MyUser(client))
    264 				continue; /* reject the kick (note: we never block remote kicks) */
    265 		}
    266 
    267 		if (ret == EX_DENY)
    268 		{
    269 			/* If set it means 'not allowed to kick'.. now check if (s)he can override that.. */
    270 			if (op_can_override("channel:override:kick:no-ops",client,channel,NULL))
    271 			{
    272 				kick_operoverride_msg(client, channel, target, comment);
    273 				goto attack; /* all other checks don't matter anymore (and could cause double msgs) */
    274 			} else {
    275 				/* Not an oper overriding */
    276 				if (MyUser(client) && badkick)
    277 					sendto_one(client, NULL, "%s", badkick); /* send error, if any */
    278 
    279 				continue; /* reject the kick */
    280 			}
    281 		}
    282 
    283 		// FIXME: Most, maybe even all, of these must be moved to HOOKTYPE_CAN_KICK checks in the corresponding halfop/chanop/chanadmin/chanowner modules :)
    284 		// !!!! FIXME
    285 
    286 		/* we are neither +o nor +h, OR..
    287 		 * we are +h but target is +o, OR...
    288 		 * we are +h and target is +h
    289 		 */
    290 		if (op_can_override("channel:override:kick:no-ops",client,channel,NULL))
    291 		{
    292 			if ((!check_channel_access_string(client_member_modes, "o") && !check_channel_access_string(client_member_modes, "h")) ||
    293 			    (check_channel_access_string(client_member_modes, "h") && check_channel_access_string(target_member_modes, "h")) ||
    294 			    (check_channel_access_string(client_member_modes, "h") && check_channel_access_string(target_member_modes, "o")))
    295 			{
    296 				kick_operoverride_msg(client, channel, target, comment);
    297 				goto attack;
    298 			}	/* is_chan_op */
    299 
    300 		}
    301 
    302 		/* target is +a/+q, and we are not +q? */
    303 		if (check_channel_access_string(target_member_modes, "qa") && !check_channel_access_string(client_member_modes, "q"))
    304 		{
    305 			if (client == target)
    306 				goto attack; /* kicking self == ok */
    307 			if (op_can_override("channel:override:kick:owner",client,channel,NULL)) /* (and f*ck local ops) */
    308 			{
    309 				/* IRCop kicking owner/prot */
    310 				kick_operoverride_msg(client, channel, target, comment);
    311 				goto attack;
    312 			}
    313 			else if (!IsULine(client) && (target != client) && MyUser(client))
    314 			{
    315 				char errbuf[NICKLEN+25];
    316 				if (check_channel_access_string(target_member_modes, "q"))
    317 					ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel owner", target->name);
    318 				else
    319 					ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel admin", target->name);
    320 				sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK", errbuf);
    321 				goto deny;
    322 			}
    323 		}
    324 
    325 		/* target is +o, we are +h [operoverride is already taken care of 2 blocks above] */
    326 		if (check_channel_access_string(target_member_modes, "h") && check_channel_access_string(client_member_modes, "h")
    327 		    && !check_channel_access_string(client_member_modes, "o") && !IsULine(client) && MyUser(client))
    328 		{
    329 			char errbuf[NICKLEN+30];
    330 			ircsnprintf(errbuf, sizeof(errbuf), "%s is a channel operator", target->name);
    331 			sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK",
    332 				   errbuf);
    333 			goto deny;
    334 		}
    335 
    336 		/* target is +h, we are +h [operoverride is already taken care of 3 blocks above] */
    337 		if (check_channel_access_string(target_member_modes, "o") && check_channel_access_string(client_member_modes, "h")
    338 		    && !check_channel_access_string(client_member_modes, "o") && MyUser(client))
    339 		{
    340 			char errbuf[NICKLEN+15];
    341 			ircsnprintf(errbuf, sizeof(errbuf), "%s is a halfop", target->name);
    342 			sendnumeric(client, ERR_CANNOTDOCOMMAND, "KICK",
    343 				   errbuf);
    344 			goto deny;
    345 		}	/* halfop */
    346 
    347 		/* allowed (either coz access granted or a remote kick), so attack! */
    348 		goto attack;
    349 
    350 	      deny:
    351 		continue;
    352 
    353 	      attack:
    354 		if (MyConnect(client)) {
    355 			int breakit = 0;
    356 			Hook *h;
    357 			for (h = Hooks[HOOKTYPE_PRE_LOCAL_KICK]; h; h = h->next) {
    358 				if ((*(h->func.intfunc))(client,target,channel,comment) > 0) {
    359 					breakit = 1;
    360 					break;
    361 				}
    362 			}
    363 			if (breakit)
    364 				continue;
    365 		}
    366 
    367 		kick_user(recv_mtags, channel, client, target, comment);
    368 	}
    369 }