unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
svsmode.c (17906B)
1 /* 2 * IRC - Internet Relay Chat, src/modules/svsmode.c 3 * (C) 2001 The UnrealIRCd Team 4 * 5 * SVSMODE and SVS2MODE commands 6 * 7 * See file AUTHORS in IRC package for additional names of 8 * the programmers. 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 1, or (at your option) 13 * any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 */ 24 25 /* FIXME: this one needs a lot more mtag work !! with _special... */ 26 27 #include "unrealircd.h" 28 29 void add_send_mode_param(Channel *channel, Client *from, char what, char mode, char *param); 30 CMD_FUNC(cmd_svsmode); 31 CMD_FUNC(cmd_svs2mode); 32 33 #define MSG_SVSMODE "SVSMODE" 34 #define MSG_SVS2MODE "SVS2MODE" 35 36 ModuleHeader MOD_HEADER 37 = { 38 "svsmode", 39 "5.0", 40 "command /svsmode and svs2mode", 41 "UnrealIRCd Team", 42 "unrealircd-6", 43 }; 44 45 char modebuf[BUFSIZE], parabuf[BUFSIZE]; 46 47 MOD_INIT() 48 { 49 CommandAdd(modinfo->handle, MSG_SVSMODE, cmd_svsmode, MAXPARA, CMD_SERVER|CMD_USER); 50 CommandAdd(modinfo->handle, MSG_SVS2MODE, cmd_svs2mode, MAXPARA, CMD_SERVER|CMD_USER); 51 MARK_AS_OFFICIAL_MODULE(modinfo); 52 return MOD_SUCCESS; 53 } 54 55 MOD_LOAD() 56 { 57 return MOD_SUCCESS; 58 } 59 60 MOD_UNLOAD() 61 { 62 return MOD_SUCCESS; 63 } 64 65 void unban_user(Client *client, Channel *channel, Client *acptr, char chmode) 66 { 67 Extban *extban; 68 const char *nextbanstr; 69 Ban *ban, *bnext; 70 Ban **banlist; 71 BanContext *b; 72 char uhost[NICKLEN+USERLEN+HOSTLEN+6], vhost[NICKLEN+USERLEN+HOSTLEN+6]; 73 char ihost[NICKLEN+USERLEN+HOSTLEN+6], chost[NICKLEN+USERLEN+HOSTLEN+6]; 74 75 /* BUILD HOSTS */ 76 77 *uhost = *vhost = *ihost = *chost = '\0'; 78 79 strlcpy(uhost, make_nick_user_host(acptr->name, 80 acptr->user->username, acptr->user->realhost), 81 sizeof uhost); 82 83 if (GetIP(acptr)) /* only if we actually have an IP */ 84 strlcpy(ihost, make_nick_user_host(acptr->name, 85 acptr->user->username, GetIP(acptr)), 86 sizeof ihost); 87 88 /* The next could have been an IsSetHost(), but I'm playing it safe with regards to backward compat. */ 89 if (IsHidden(acptr) && 90 !(*acptr->user->cloakedhost && !strcasecmp(acptr->user->virthost, acptr->user->cloakedhost))) 91 { 92 strlcpy(vhost, make_nick_user_host(acptr->name, 93 acptr->user->username, acptr->user->virthost), 94 sizeof vhost); 95 } 96 97 if (*acptr->user->cloakedhost) /* only if we know the cloaked host */ 98 strlcpy(chost, make_nick_user_host(acptr->name, 99 acptr->user->username, acptr->user->cloakedhost), 100 sizeof chost); 101 102 /* SELECT BANLIST */ 103 104 switch (chmode) 105 { 106 case 'b': 107 banlist = &channel->banlist; 108 break; 109 case 'e': 110 banlist = &channel->exlist; 111 break; 112 case 'I': 113 banlist = &channel->invexlist; 114 break; 115 default: 116 abort(); 117 } 118 119 /* DO THE ACTUAL WORK */ 120 121 b = safe_alloc(sizeof(BanContext)); 122 b->client = acptr; 123 b->channel = channel; 124 b->ban_check_types = BANCHK_JOIN; 125 126 for (ban = *banlist; ban; ban = bnext) 127 { 128 bnext = ban->next; 129 if (match_simple(ban->banstr, uhost) || 130 (*vhost && match_simple(ban->banstr, vhost)) || 131 (*ihost && match_simple(ban->banstr, ihost)) || 132 (*chost && match_simple(ban->banstr, chost))) 133 { 134 add_send_mode_param(channel, client, '-', chmode, ban->banstr); 135 del_listmode(banlist, channel, ban->banstr); 136 } 137 else if (chmode != 'I' && *ban->banstr == '~' && (extban = findmod_by_bantype(ban->banstr, &nextbanstr))) 138 { 139 if (extban->is_banned_events & b->ban_check_types) 140 { 141 b->banstr = nextbanstr; 142 if (extban->is_banned(b)) 143 { 144 add_send_mode_param(channel, acptr, '-', chmode, ban->banstr); 145 del_listmode(banlist, channel, ban->banstr); 146 } 147 } 148 } 149 } 150 safe_free(b); 151 } 152 153 void clear_bans(Client *client, Channel *channel, char chmode) 154 { 155 Extban *extban; 156 Ban *ban, *bnext; 157 Ban **banlist; 158 159 switch (chmode) 160 { 161 case 'b': 162 banlist = &channel->banlist; 163 break; 164 case 'e': 165 banlist = &channel->exlist; 166 break; 167 case 'I': 168 banlist = &channel->invexlist; 169 break; 170 default: 171 abort(); 172 } 173 174 for (ban = *banlist; ban; ban = bnext) 175 { 176 bnext = ban->next; 177 if (chmode != 'I' && (*ban->banstr == '~') && (extban = findmod_by_bantype(ban->banstr, NULL))) 178 { 179 if (!(extban->is_banned_events & BANCHK_JOIN)) 180 continue; 181 } 182 add_send_mode_param(channel, client, '-', chmode, ban->banstr); 183 del_listmode(banlist, channel, ban->banstr); 184 } 185 } 186 187 /** Special Channel MODE command for services, used by SVSMODE and SVS2MODE. 188 * @note 189 * This SVSMODE/SVS2MODE for channels is not simply the regular MODE "but for 190 * services". No, it does different things. 191 * 192 * Syntax: SVSMODE <channel> <mode and params> 193 * 194 * There are three variants (do NOT mix them!): 195 * 1) SVSMODE #chan -[b|e|I] 196 * This will remove all bans/exempts/invex in the channel. 197 * 2) SVSMODE #chan -[b|e|I] nickname 198 * This will remove all bans/exempts/invex that affect nickname. 199 * Eg: -b nick may remove bans places on IP, host, cloaked host, vhost, 200 * basically anything that matches this nickname. Note that the 201 * user with the specified nickname needs to be online for this to work. 202 * 3) SVSMODE #chan -[v|h|o|a|q] 203 * This will remove the specified mode(s) from all users in the channel. 204 * Eg: -o will result in a MODE -o for every channel operator. 205 * 206 * OLD syntax had a 'ts' parameter. No services are known to use this. 207 */ 208 void channel_svsmode(Client *client, int parc, const char *parv[]) 209 { 210 Channel *channel; 211 Client *target; 212 const char *m; 213 int what = MODE_ADD; 214 int i = 4; // wtf is this 215 Member *member; 216 int channel_flags; 217 218 *parabuf = *modebuf = '\0'; 219 220 if ((parc < 3) || BadPtr(parv[2])) 221 return; 222 223 if (!(channel = find_channel(parv[1]))) 224 return; 225 226 for (m = parv[2]; *m; m++) 227 { 228 if (*m == '+') 229 { 230 what = MODE_ADD; 231 } else 232 if (*m == '-') 233 { 234 what = MODE_DEL; 235 } else 236 if ((*m == 'b') || (*m == 'e') || (*m == 'I')) 237 { 238 if (parc >= i) 239 { 240 if (!(target = find_user(parv[i-1], NULL))) 241 { 242 i++; 243 break; 244 } 245 i++; 246 247 unban_user(client, channel, target, *m); 248 } 249 else { 250 clear_bans(client, channel, *m); 251 } 252 } else 253 { 254 /* Find member mode handler (vhoaq) */ 255 Cmode *cm = find_channel_mode_handler(*m); 256 if (!cm || (cm->type != CMODE_MEMBER)) 257 { 258 unreal_log(ULOG_WARNING, "svsmode", "INVALID_SVSMODE", client, 259 "Invalid SVSMODE for mode '$mode_character' in channel $channel from $client.", 260 log_data_char("mode_character", *m), 261 log_data_channel("channel", channel)); 262 continue; 263 } 264 if (what != MODE_DEL) 265 { 266 unreal_log(ULOG_WARNING, "svsmode", "INVALID_SVSMODE", client, 267 "Invalid SVSMODE from $client trying to add '$mode_character' in $channel.", 268 log_data_char("mode_character", *m), 269 log_data_channel("channel", channel)); 270 continue; 271 } 272 for (member = channel->members; member; member = member->next) 273 { 274 if (check_channel_access_letter(member->member_modes, *m)) 275 { 276 Membership *mb = find_membership_link(member->client->user->channel, channel); 277 if (!mb) 278 continue; /* bug */ 279 280 /* Send the -x out */ 281 add_send_mode_param(channel, client, '-', *m, member->client->name); 282 283 /* And remove from memory */ 284 del_member_mode_fast(member, mb, *m); 285 } 286 } 287 } 288 } 289 290 /* only send message if modes have changed */ 291 if (*parabuf) 292 { 293 MessageTag *mtags = NULL; 294 int destroy_channel = 0; 295 /* NOTE: cannot use 'recv_mtag' here because MODE could be rewrapped. Not ideal :( */ 296 new_message(client, NULL, &mtags); 297 298 sendto_channel(channel, client, client, 0, 0, SEND_LOCAL, mtags, 299 ":%s MODE %s %s %s", 300 client->name, channel->name, modebuf, parabuf); 301 sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s%s", client->id, channel->name, modebuf, parabuf, IsServer(client)?" 0":""); 302 303 /* Activate this hook just like cmd_mode.c */ 304 RunHook(HOOKTYPE_REMOTE_CHANMODE, client, channel, mtags, modebuf, parabuf, 0, 0, &destroy_channel); 305 306 free_message_tags(mtags); 307 308 *parabuf = 0; 309 } 310 } 311 312 /** Special User MODE command for Services. 313 * @note 314 * This is used by both SVSMODE and SVS2MODE, when dealing with users (not channels). 315 * parv[1] - nick to change mode for 316 * parv[2] - modes to change 317 * parv[3] - account name (if mode contains 'd') 318 * 319 * show_change can be 0 (for svsmode) or 1 (for svs2mode). 320 */ 321 void do_svsmode(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], int show_change) 322 { 323 Umode *um; 324 const char *m; 325 Client *target; 326 int what; 327 long oldumodes = 0; 328 329 if (!IsSvsCmdOk(client)) 330 return; 331 332 what = MODE_ADD; 333 334 if (parc < 3) 335 return; 336 337 if (parv[1][0] == '#') 338 { 339 channel_svsmode(client, parc, parv); 340 return; 341 } 342 343 if (!(target = find_user(parv[1], NULL))) 344 return; 345 346 userhost_save_current(target); 347 348 oldumodes = target->umodes; 349 350 /* parse mode change string(s) */ 351 for (m = parv[2]; *m; m++) 352 switch (*m) 353 { 354 case '+': 355 what = MODE_ADD; 356 break; 357 case '-': 358 what = MODE_DEL; 359 break; 360 361 /* we may not get these, but they shouldnt be in default */ 362 case ' ': 363 case '\n': 364 case '\r': 365 case '\t': 366 break; 367 case 'i': 368 if ((what == MODE_ADD) && !(target->umodes & UMODE_INVISIBLE)) 369 irccounts.invisible++; 370 if ((what == MODE_DEL) && (target->umodes & UMODE_INVISIBLE)) 371 irccounts.invisible--; 372 goto setmodex; 373 case 'o': 374 if ((what == MODE_ADD) && !(target->umodes & UMODE_OPER)) 375 { 376 if (!IsOper(target) && MyUser(target)) 377 list_add(&target->special_node, &oper_list); 378 379 irccounts.operators++; 380 } 381 if ((what == MODE_DEL) && (target->umodes & UMODE_OPER)) 382 { 383 if (target->umodes & UMODE_HIDEOPER) 384 { 385 /* clear 'H' too, and opercount stays the same.. */ 386 target->umodes &= ~UMODE_HIDEOPER; 387 } else { 388 irccounts.operators--; 389 } 390 391 if (MyUser(target) && !list_empty(&target->special_node)) 392 list_del(&target->special_node); 393 394 /* User is no longer oper (after the goto below, anyway)... 395 * so remove all oper-only modes and snomasks. 396 */ 397 if (MyUser(client)) 398 RunHook(HOOKTYPE_LOCAL_OPER, client, 0, NULL, NULL); 399 remove_oper_privileges(target, 0); 400 } 401 goto setmodex; 402 case 'H': 403 if (what == MODE_ADD && !(target->umodes & UMODE_HIDEOPER)) 404 { 405 if (!IsOper(target) && !strchr(parv[2], 'o')) /* (ofcoz this strchr() is flawed) */ 406 { 407 /* isn't an oper, and would not become one either.. abort! */ 408 unreal_log(ULOG_WARNING, "svsmode", "SVSMODE_INVALID", client, 409 "[BUG] Server $client tried to set user mode +H (hidden ircop) " 410 "on a user that is not +o (not ircop)! " 411 "Please fix your services, or if you think it is our fault, then " 412 "report at https://bugs.unrealircd.org/. " 413 "Parameters: $para1 $para2. Target: $target.", 414 log_data_string("para1", parv[1]), 415 log_data_string("para2", parv[2]), 416 log_data_client("target", target)); 417 break; /* abort! */ 418 } 419 irccounts.operators--; 420 } 421 if (what == MODE_DEL && (target->umodes & UMODE_HIDEOPER)) 422 irccounts.operators++; 423 goto setmodex; 424 case 'd': 425 if (parv[3]) 426 { 427 int was_logged_in = IsLoggedIn(target) ? 1 : 0; 428 strlcpy(target->user->account, parv[3], sizeof(target->user->account)); 429 if (!was_logged_in && !IsLoggedIn(target)) 430 { 431 /* We don't care about users going from not logged in 432 * to not logged in, which is something that can happen 433 * from 0 to 123456, eg from no account to unconfirmed account. 434 */ 435 } else { 436 /* LOGIN or LOGOUT (or account change) */ 437 user_account_login(recv_mtags, target); 438 } 439 if (MyConnect(target) && IsDead(target)) 440 return; /* was killed due to *LINE on ~a probably */ 441 } 442 else 443 { 444 /* setting deaf */ 445 goto setmodex; 446 } 447 break; 448 case 'x': 449 if (what == MODE_DEL) 450 { 451 /* -x */ 452 if (target->user->virthost) 453 { 454 /* Removing mode +x and virthost set... recalculate host then (but don't activate it!) */ 455 safe_strdup(target->user->virthost, target->user->cloakedhost); 456 } 457 } else 458 { 459 /* +x */ 460 if (!target->user->virthost) 461 { 462 /* Hmm... +x but no virthost set, that's bad... use cloakedhost. 463 * Not sure if this could ever happen, but just in case... -- Syzop 464 */ 465 safe_strdup(target->user->virthost, target->user->cloakedhost); 466 } 467 /* Announce the new host to VHP servers if we're setting the virthost to the cloakedhost. 468 * In other cases, we can assume that the host has been broadcasted already (after all, 469 * how else could it have been changed...?). 470 * NOTES: we're doing a strcasecmp here instead of simply checking if it's a "+x but 471 * not -t"-case. The reason for this is that the 't' might follow ("+xt" instead of "+tx"), 472 * in which case we would have needlessly announced it. Ok I didn't test it but that's 473 * the idea behind it :P. -- Syzop 474 */ 475 if (MyUser(target) && !strcasecmp(target->user->virthost, target->user->cloakedhost)) 476 sendto_server(NULL, PROTO_VHP, 0, NULL, ":%s SETHOST :%s", target->id, 477 target->user->virthost); 478 } 479 goto setmodex; 480 case 't': 481 /* We support -t nowadays, which means we remove the vhost and set the cloaked host 482 * (note that +t is a NOOP, that code is in +x) 483 */ 484 if (what == MODE_DEL) 485 { 486 /* First, check if there's a change at all. Perhaps it's a -t on someone 487 * with no virthost or virthost being cloakedhost? 488 * Also, check to make sure user->cloakedhost exists at all. 489 * This so we won't crash in weird cases like non-conformant servers. 490 */ 491 if (target->user->virthost && *target->user->cloakedhost && strcasecmp(target->user->cloakedhost, GetHost(target))) 492 { 493 /* Make the change effective: */ 494 safe_strdup(target->user->virthost, target->user->cloakedhost); 495 /* And broadcast the change to VHP servers */ 496 if (MyUser(target)) 497 sendto_server(NULL, PROTO_VHP, 0, NULL, ":%s SETHOST :%s", target->id, 498 target->user->virthost); 499 } 500 goto setmodex; 501 } 502 break; 503 case 'z': 504 /* Setting and unsetting user mode 'z' remotely is not supported */ 505 break; 506 default: 507 setmodex: 508 for (um = usermodes; um; um = um->next) 509 { 510 if (um->letter == *m) 511 { 512 if (what == MODE_ADD) 513 target->umodes |= um->mode; 514 else 515 target->umodes &= ~um->mode; 516 break; 517 } 518 } 519 break; 520 } /*switch*/ 521 522 if (parc > 3) 523 sendto_server(client, 0, 0, recv_mtags, ":%s %s %s %s %s", 524 client->id, show_change ? "SVS2MODE" : "SVSMODE", 525 parv[1], parv[2], parv[3]); 526 else 527 sendto_server(client, 0, 0, recv_mtags, ":%s %s %s %s", 528 client->id, show_change ? "SVS2MODE" : "SVSMODE", 529 parv[1], parv[2]); 530 531 /* Here we trigger the same hooks that cmd_mode does and, likewise, 532 only if the old flags (oldumodes) are different than the newly- 533 set ones */ 534 if (oldumodes != target->umodes) 535 RunHook(HOOKTYPE_UMODE_CHANGE, target, oldumodes, target->umodes); 536 537 if (show_change) 538 { 539 char buf[BUFSIZE]; 540 build_umode_string(target, oldumodes, ALL_UMODES, buf); 541 if (MyUser(target) && *buf) 542 { 543 MessageTag *mtags = NULL; 544 new_message(client, recv_mtags, &mtags); 545 sendto_one(target, mtags, ":%s MODE %s :%s", client->name, target->name, buf); 546 safe_free_message_tags(mtags); 547 } 548 } 549 550 userhost_changed(target); /* we can safely call this, even if nothing changed */ 551 552 VERIFY_OPERCOUNT(target, "svsmodeX"); 553 } 554 555 /* 556 * cmd_svsmode() added by taz 557 * parv[1] - username to change mode for 558 * parv[2] - modes to change 559 * parv[3] - account name (if mode contains 'd') 560 */ 561 CMD_FUNC(cmd_svsmode) 562 { 563 do_svsmode(client, recv_mtags, parc, parv, 0); 564 } 565 566 /* 567 * cmd_svs2mode() added by Potvin 568 * parv[1] - username to change mode for 569 * parv[2] - modes to change 570 * parv[3] - account name (if mode contains 'd') 571 */ 572 CMD_FUNC(cmd_svs2mode) 573 { 574 do_svsmode(client, recv_mtags, parc, parv, 1); 575 } 576 577 void add_send_mode_param(Channel *channel, Client *from, char what, char mode, char *param) 578 { 579 static char *modes = NULL, lastwhat; 580 static short count = 0; 581 short send = 0; 582 583 if (!modes) modes = modebuf; 584 585 if (!modebuf[0]) 586 { 587 modes = modebuf; 588 *modes++ = what; 589 *modes = 0; 590 lastwhat = what; 591 *parabuf = 0; 592 count = 0; 593 } 594 if (lastwhat != what) 595 { 596 *modes++ = what; 597 *modes = 0; 598 lastwhat = what; 599 } 600 if (strlen(parabuf) + strlen(param) + 11 < MODEBUFLEN) 601 { 602 if (*parabuf) 603 strcat(parabuf, " "); 604 strcat(parabuf, param); 605 *modes++ = mode; 606 *modes = 0; 607 count++; 608 } 609 else if (*parabuf) 610 send = 1; 611 612 if (count == MAXMODEPARAMS) 613 send = 1; 614 615 if (send) 616 { 617 MessageTag *mtags = NULL; 618 /* NOTE: cannot use 'recv_mtag' here because MODE could be rewrapped. Not ideal :( */ 619 new_message(from, NULL, &mtags); 620 sendto_channel(channel, from, from, 0, 0, SEND_LOCAL, mtags, 621 ":%s MODE %s %s %s", 622 from->name, channel->name, modebuf, parabuf); 623 sendto_server(NULL, 0, 0, mtags, ":%s MODE %s %s %s%s", from->id, channel->name, modebuf, parabuf, IsServer(from)?" 0":""); 624 free_message_tags(mtags); 625 send = 0; 626 *parabuf = 0; 627 modes = modebuf; 628 *modes++ = what; 629 lastwhat = what; 630 if (count != MAXMODEPARAMS) 631 { 632 strcpy(parabuf, param); 633 *modes++ = mode; 634 count = 1; 635 } 636 else 637 count = 0; 638 *modes = 0; 639 } 640 }