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 }