anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
cs_mode.cpp (28211B)
1 /* ChanServ core functions 2 * 3 * (C) 2003-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 * 8 * Based on the original code of Epona by Lara. 9 * Based on the original code of Services by Andy Church. 10 */ 11 12 #include "module.h" 13 #include "modules/cs_mode.h" 14 15 struct ModeLockImpl : ModeLock, Serializable 16 { 17 ModeLockImpl() : Serializable("ModeLock") 18 { 19 } 20 21 ~ModeLockImpl() 22 { 23 ChannelInfo *chan = ChannelInfo::Find(ci); 24 if (chan) 25 { 26 ModeLocks *ml = chan->GetExt<ModeLocks>("modelocks"); 27 if (ml) 28 ml->RemoveMLock(this); 29 } 30 } 31 32 void Serialize(Serialize::Data &data) const anope_override; 33 static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); 34 }; 35 36 struct ModeLocksImpl : ModeLocks 37 { 38 Serialize::Reference<ChannelInfo> ci; 39 Serialize::Checker<ModeList> mlocks; 40 41 ModeLocksImpl(Extensible *obj) : ci(anope_dynamic_static_cast<ChannelInfo *>(obj)), mlocks("ModeLock") 42 { 43 } 44 45 ~ModeLocksImpl() 46 { 47 ModeList modelist; 48 mlocks->swap(modelist); 49 for (ModeList::iterator it = modelist.begin(); it != modelist.end(); ++it) 50 { 51 ModeLock *ml = *it; 52 delete ml; 53 } 54 } 55 56 bool HasMLock(ChannelMode *mode, const Anope::string ¶m, bool status) const anope_override 57 { 58 if (!mode) 59 return false; 60 61 for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) 62 { 63 const ModeLock *ml = *it; 64 65 if (ml->name == mode->name && ml->set == status && ml->param == param) 66 return true; 67 } 68 69 return false; 70 } 71 72 bool SetMLock(ChannelMode *mode, bool status, const Anope::string ¶m, Anope::string setter, time_t created = Anope::CurTime) anope_override 73 { 74 if (!mode) 75 return false; 76 77 RemoveMLock(mode, status, param); 78 79 if (setter.empty()) 80 setter = ci->GetFounder() ? ci->GetFounder()->display : "Unknown"; 81 82 ModeLock *ml = new ModeLockImpl(); 83 ml->ci = ci->name; 84 ml->set = status; 85 ml->name = mode->name; 86 ml->param = param; 87 ml->setter = setter; 88 ml->created = created; 89 90 EventReturn MOD_RESULT; 91 FOREACH_RESULT(OnMLock, MOD_RESULT, (this->ci, ml)); 92 if (MOD_RESULT == EVENT_STOP) 93 { 94 delete ml; 95 return false; 96 } 97 98 this->mlocks->push_back(ml); 99 return true; 100 } 101 102 bool RemoveMLock(ChannelMode *mode, bool status, const Anope::string ¶m = "") anope_override 103 { 104 if (!mode) 105 return false; 106 107 for (ModeList::iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) 108 { 109 ModeLock *m = *it; 110 111 if (m->name == mode->name) 112 { 113 // For list or status modes, we must check the parameter 114 if (mode->type == MODE_LIST || mode->type == MODE_STATUS) 115 if (m->param != param) 116 continue; 117 118 EventReturn MOD_RESULT; 119 FOREACH_RESULT(OnUnMLock, MOD_RESULT, (this->ci, m)); 120 if (MOD_RESULT == EVENT_STOP) 121 break; 122 123 delete m; 124 return true; 125 } 126 } 127 128 return false; 129 } 130 131 void RemoveMLock(ModeLock *mlock) anope_override 132 { 133 ModeList::iterator it = std::find(this->mlocks->begin(), this->mlocks->end(), mlock); 134 if (it != this->mlocks->end()) 135 this->mlocks->erase(it); 136 } 137 138 void ClearMLock() anope_override 139 { 140 ModeList ml; 141 this->mlocks->swap(ml); 142 for (unsigned i = 0; i < ml.size(); ++i) 143 delete ml[i]; 144 } 145 146 const ModeList &GetMLock() const anope_override 147 { 148 return this->mlocks; 149 } 150 151 std::list<ModeLock *> GetModeLockList(const Anope::string &name) anope_override 152 { 153 std::list<ModeLock *> mlist; 154 for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) 155 { 156 ModeLock *m = *it; 157 if (m->name == name) 158 mlist.push_back(m); 159 } 160 return mlist; 161 } 162 163 const ModeLock *GetMLock(const Anope::string &mname, const Anope::string ¶m = "") anope_override 164 { 165 for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) 166 { 167 ModeLock *m = *it; 168 169 if (m->name == mname && m->param == param) 170 return m; 171 } 172 173 return NULL; 174 } 175 176 Anope::string GetMLockAsString(bool complete) const anope_override 177 { 178 Anope::string pos = "+", neg = "-", params; 179 180 for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) 181 { 182 const ModeLock *ml = *it; 183 ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); 184 185 if (!cm || cm->type == MODE_LIST || cm->type == MODE_STATUS) 186 continue; 187 188 if (ml->set) 189 pos += cm->mchar; 190 else 191 neg += cm->mchar; 192 193 if (complete && ml->set && !ml->param.empty() && cm->type == MODE_PARAM) 194 params += " " + ml->param; 195 } 196 197 if (pos.length() == 1) 198 pos.clear(); 199 if (neg.length() == 1) 200 neg.clear(); 201 202 return pos + neg + params; 203 } 204 205 void Check() anope_override 206 { 207 if (this->mlocks->empty()) 208 ci->Shrink<ModeLocks>("modelocks"); 209 } 210 }; 211 212 void ModeLockImpl::Serialize(Serialize::Data &data) const 213 { 214 data["ci"] << this->ci; 215 data["set"] << this->set; 216 data["name"] << this->name; 217 data["param"] << this->param; 218 data["setter"] << this->setter; 219 data.SetType("created", Serialize::Data::DT_INT); data["created"] << this->created; 220 } 221 222 Serializable* ModeLockImpl::Unserialize(Serializable *obj, Serialize::Data &data) 223 { 224 Anope::string sci; 225 226 data["ci"] >> sci; 227 228 ChannelInfo *ci = ChannelInfo::Find(sci); 229 if (!ci) 230 return NULL; 231 232 ModeLockImpl *ml; 233 if (obj) 234 ml = anope_dynamic_static_cast<ModeLockImpl *>(obj); 235 else 236 { 237 ml = new ModeLockImpl(); 238 ml->ci = ci->name; 239 } 240 241 data["set"] >> ml->set; 242 data["created"] >> ml->created; 243 data["setter"] >> ml->setter; 244 data["name"] >> ml->name; 245 data["param"] >> ml->param; 246 247 if (!obj) 248 ci->Require<ModeLocksImpl>("modelocks")->mlocks->push_back(ml); 249 250 return ml; 251 } 252 253 class CommandCSMode : public Command 254 { 255 bool CanSet(CommandSource &source, ChannelInfo *ci, ChannelMode *cm, bool self) 256 { 257 if (!ci || !cm || cm->type != MODE_STATUS) 258 return false; 259 260 return source.AccessFor(ci).HasPriv(cm->name + (self ? "ME" : "")); 261 } 262 263 void DoLock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 264 { 265 User *u = source.GetUser(); 266 const Anope::string &subcommand = params[2]; 267 const Anope::string ¶m = params.size() > 3 ? params[3] : ""; 268 269 bool override = !source.AccessFor(ci).HasPriv("MODE"); 270 ModeLocks *modelocks = ci->Require<ModeLocks>("modelocks"); 271 272 if (Anope::ReadOnly && !subcommand.equals_ci("LIST")) 273 { 274 source.Reply(READ_ONLY_MODE); 275 return; 276 } 277 278 if ((subcommand.equals_ci("ADD") || subcommand.equals_ci("SET")) && !param.empty()) 279 { 280 /* If setting, remove the existing locks */ 281 if (subcommand.equals_ci("SET")) 282 { 283 const ModeLocks::ModeList mlocks = modelocks->GetMLock(); 284 for (ModeLocks::ModeList::const_iterator it = mlocks.begin(); it != mlocks.end(); ++it) 285 { 286 const ModeLock *ml = *it; 287 ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); 288 if (cm && cm->CanSet(source.GetUser())) 289 modelocks->RemoveMLock(cm, ml->set, ml->param); 290 } 291 } 292 293 spacesepstream sep(param); 294 Anope::string modes; 295 296 sep.GetToken(modes); 297 298 Anope::string pos = "+", neg = "-", pos_params, neg_params; 299 300 int adding = 1; 301 bool needreply = true; 302 for (size_t i = 0; i < modes.length(); ++i) 303 { 304 switch (modes[i]) 305 { 306 case '+': 307 adding = 1; 308 break; 309 case '-': 310 adding = 0; 311 break; 312 default: 313 needreply = false; 314 ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); 315 if (!cm) 316 { 317 source.Reply(_("Unknown mode character %c ignored."), modes[i]); 318 break; 319 } 320 else if (u && !cm->CanSet(u)) 321 { 322 source.Reply(_("You may not (un)lock mode %c."), modes[i]); 323 break; 324 } 325 326 Anope::string mode_param; 327 if (((cm->type == MODE_STATUS || cm->type == MODE_LIST) && !sep.GetToken(mode_param)) || (cm->type == MODE_PARAM && adding && !sep.GetToken(mode_param))) 328 { 329 source.Reply(_("Missing parameter for mode %c."), cm->mchar); 330 continue; 331 } 332 333 if (cm->type == MODE_STATUS && !CanSet(source, ci, cm, false)) 334 { 335 source.Reply(ACCESS_DENIED); 336 continue; 337 } 338 339 if (cm->type == MODE_LIST && ci->c && IRCD->GetMaxListFor(ci->c, cm) && ci->c->HasMode(cm->name) >= IRCD->GetMaxListFor(ci->c, cm)) 340 { 341 source.Reply(_("List for mode %c is full."), cm->mchar); 342 continue; 343 } 344 345 if (modelocks->GetMLock().size() >= Config->GetModule(this->owner)->Get<unsigned>("max", "32")) 346 { 347 source.Reply(_("The mode lock list of \002%s\002 is full."), ci->name.c_str()); 348 continue; 349 } 350 351 modelocks->SetMLock(cm, adding, mode_param, source.GetNick()); 352 353 if (adding) 354 { 355 pos += cm->mchar; 356 if (!mode_param.empty()) 357 pos_params += " " + mode_param; 358 } 359 else 360 { 361 neg += cm->mchar; 362 if (!mode_param.empty()) 363 neg_params += " " + mode_param; 364 } 365 } 366 } 367 368 if (pos == "+") 369 pos.clear(); 370 if (neg == "-") 371 neg.clear(); 372 Anope::string reply = pos + neg + pos_params + neg_params; 373 374 if (!reply.empty()) 375 { 376 source.Reply(_("%s locked on %s."), reply.c_str(), ci->name.c_str()); 377 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to lock " << reply; 378 } 379 else if (needreply) 380 source.Reply(_("Nothing to do.")); 381 382 if (ci->c) 383 ci->c->CheckModes(); 384 } 385 else if (subcommand.equals_ci("DEL") && !param.empty()) 386 { 387 spacesepstream sep(param); 388 Anope::string modes; 389 390 sep.GetToken(modes); 391 392 int adding = 1; 393 bool needreply = true; 394 for (size_t i = 0; i < modes.length(); ++i) 395 { 396 switch (modes[i]) 397 { 398 case '+': 399 adding = 1; 400 break; 401 case '-': 402 adding = 0; 403 break; 404 default: 405 needreply = false; 406 ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); 407 if (!cm) 408 { 409 source.Reply(_("Unknown mode character %c ignored."), modes[i]); 410 break; 411 } 412 else if (u && !cm->CanSet(u)) 413 { 414 source.Reply(_("You may not (un)lock mode %c."), modes[i]); 415 break; 416 } 417 418 Anope::string mode_param; 419 if (cm->type != MODE_REGULAR && !sep.GetToken(mode_param)) 420 source.Reply(_("Missing parameter for mode %c."), cm->mchar); 421 else 422 { 423 if (modelocks->RemoveMLock(cm, adding, mode_param)) 424 { 425 if (!mode_param.empty()) 426 mode_param = " " + mode_param; 427 source.Reply(_("%c%c%s has been unlocked from %s."), adding == 1 ? '+' : '-', cm->mchar, mode_param.c_str(), ci->name.c_str()); 428 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to unlock " << (adding ? '+' : '-') << cm->mchar << mode_param; 429 } 430 else 431 source.Reply(_("%c%c is not locked on %s."), adding == 1 ? '+' : '-', cm->mchar, ci->name.c_str()); 432 } 433 } 434 } 435 436 if (needreply) 437 source.Reply(_("Nothing to do.")); 438 } 439 else if (subcommand.equals_ci("LIST")) 440 { 441 const ModeLocks::ModeList mlocks = modelocks->GetMLock(); 442 if (mlocks.empty()) 443 { 444 source.Reply(_("Channel %s has no mode locks."), ci->name.c_str()); 445 } 446 else 447 { 448 ListFormatter list(source.GetAccount()); 449 list.AddColumn(_("Mode")).AddColumn(_("Param")).AddColumn(_("Creator")).AddColumn(_("Created")); 450 451 for (ModeLocks::ModeList::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it) 452 { 453 const ModeLock *ml = *it; 454 ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); 455 if (!cm) 456 continue; 457 458 ListFormatter::ListEntry entry; 459 entry["Mode"] = Anope::printf("%c%c", ml->set ? '+' : '-', cm->mchar); 460 entry["Param"] = ml->param; 461 entry["Creator"] = ml->setter; 462 entry["Created"] = Anope::strftime(ml->created, NULL, true); 463 list.AddEntry(entry); 464 } 465 466 source.Reply(_("Mode locks for %s:"), ci->name.c_str()); 467 468 std::vector<Anope::string> replies; 469 list.Process(replies); 470 471 for (unsigned i = 0; i < replies.size(); ++i) 472 source.Reply(replies[i]); 473 } 474 } 475 else 476 this->OnSyntaxError(source, subcommand); 477 } 478 479 void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 480 { 481 User *u = source.GetUser(); 482 483 bool has_access = source.AccessFor(ci).HasPriv("MODE") || source.HasPriv("chanserv/administration"); 484 bool can_override = source.HasPriv("chanserv/administration"); 485 486 spacesepstream sep(params.size() > 3 ? params[3] : ""); 487 Anope::string modes = params[2], param; 488 489 bool override = !source.AccessFor(ci).HasPriv("MODE") && source.HasPriv("chanserv/administration"); 490 491 int adding = -1; 492 for (size_t i = 0; i < modes.length(); ++i) 493 { 494 switch (modes[i]) 495 { 496 case '+': 497 adding = 1; 498 break; 499 case '-': 500 adding = 0; 501 break; 502 case '*': 503 if (adding == -1 || !has_access) 504 break; 505 for (unsigned j = 0; j < ModeManager::GetChannelModes().size() && ci->c; ++j) 506 { 507 ChannelMode *cm = ModeManager::GetChannelModes()[j]; 508 509 if (!u || cm->CanSet(u) || can_override) 510 { 511 if (cm->type == MODE_REGULAR || (!adding && cm->type == MODE_PARAM)) 512 { 513 if (adding) 514 ci->c->SetMode(NULL, cm); 515 else 516 ci->c->RemoveMode(NULL, cm); 517 } 518 } 519 } 520 break; 521 default: 522 if (adding == -1) 523 break; 524 ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); 525 if (!cm || (u && !cm->CanSet(u) && !can_override)) 526 continue; 527 switch (cm->type) 528 { 529 case MODE_REGULAR: 530 if (!has_access) 531 break; 532 if (adding) 533 ci->c->SetMode(NULL, cm); 534 else 535 ci->c->RemoveMode(NULL, cm); 536 break; 537 case MODE_PARAM: 538 if (!has_access) 539 break; 540 if (adding && !sep.GetToken(param)) 541 break; 542 if (adding) 543 ci->c->SetMode(NULL, cm, param); 544 else 545 ci->c->RemoveMode(NULL, cm); 546 break; 547 case MODE_STATUS: 548 { 549 if (!sep.GetToken(param)) 550 param = source.GetNick(); 551 552 AccessGroup u_access = source.AccessFor(ci); 553 554 if (param.find_first_of("*?") != Anope::string::npos) 555 { 556 if (!this->CanSet(source, ci, cm, false)) 557 { 558 if (can_override) 559 { 560 override = true; 561 } 562 else 563 { 564 source.Reply(_("You do not have access to set mode %c."), cm->mchar); 565 break; 566 } 567 } 568 569 for (Channel::ChanUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end;) 570 { 571 ChanUserContainer *uc = it->second; 572 ++it; 573 574 AccessGroup targ_access = ci->AccessFor(uc->user); 575 576 if (uc->user->IsProtected()) 577 { 578 source.Reply(_("You do not have the access to change %s's modes."), uc->user->nick.c_str()); 579 continue; 580 } 581 582 if (ci->HasExt("PEACE") && targ_access >= u_access) 583 { 584 if (can_override) 585 { 586 override = true; 587 } 588 else 589 { 590 source.Reply(_("You do not have the access to change %s's modes."), uc->user->nick.c_str()); 591 continue; 592 } 593 } 594 595 if (Anope::Match(uc->user->GetMask(), param)) 596 { 597 if (adding) 598 ci->c->SetMode(NULL, cm, uc->user->GetUID()); 599 else 600 ci->c->RemoveMode(NULL, cm, uc->user->GetUID()); 601 } 602 } 603 } 604 else 605 { 606 User *target = User::Find(param, true); 607 if (target == NULL) 608 { 609 source.Reply(NICK_X_NOT_IN_USE, param.c_str()); 610 break; 611 } 612 613 if (!this->CanSet(source, ci, cm, source.GetUser() == target)) 614 { 615 if (can_override) 616 { 617 override = true; 618 } 619 else 620 { 621 source.Reply(_("You do not have access to set mode %c."), cm->mchar); 622 break; 623 } 624 } 625 626 if (source.GetUser() != target) 627 { 628 AccessGroup targ_access = ci->AccessFor(target); 629 if (ci->HasExt("PEACE") && targ_access >= u_access) 630 { 631 source.Reply(_("You do not have the access to change %s's modes."), target->nick.c_str()); 632 break; 633 } 634 else if (can_override) 635 { 636 override = true; 637 } 638 else if (target->IsProtected()) 639 { 640 source.Reply(ACCESS_DENIED); 641 break; 642 } 643 } 644 645 if (adding) 646 ci->c->SetMode(NULL, cm, target->GetUID()); 647 else 648 ci->c->RemoveMode(NULL, cm, target->GetUID()); 649 } 650 break; 651 } 652 case MODE_LIST: 653 if (!has_access) 654 break; 655 if (!sep.GetToken(param)) 656 break; 657 658 // Change to internal name, eg giving -b ~q:* 659 cm = cm->Unwrap(param); 660 661 if (adding) 662 { 663 if (IRCD->GetMaxListFor(ci->c, cm) && ci->c->HasMode(cm->name) < IRCD->GetMaxListFor(ci->c, cm)) 664 ci->c->SetMode(NULL, cm, param); 665 } 666 else 667 { 668 std::vector<Anope::string> v = ci->c->GetModeList(cm->name); 669 for (unsigned j = 0; j < v.size(); ++j) 670 if (Anope::Match(v[j], param)) 671 ci->c->RemoveMode(NULL, cm, v[j]); 672 } 673 } 674 } // switch 675 } 676 677 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << modes << (params.size() > 3 ? " " + params[3] : ""); 678 } 679 680 void DoClear(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 681 { 682 const Anope::string ¶m = params.size() > 2 ? params[2] : ""; 683 684 if (param.empty()) 685 { 686 std::vector<Anope::string> new_params; 687 new_params.push_back(params[0]); 688 new_params.push_back("SET"); 689 new_params.push_back("-*"); 690 this->DoSet(source, ci, new_params); 691 return; 692 } 693 694 ChannelMode *cm; 695 if (param.length() == 1) 696 cm = ModeManager::FindChannelModeByChar(param[0]); 697 else 698 { 699 cm = ModeManager::FindChannelModeByName(param.upper()); 700 if (!cm) 701 cm = ModeManager::FindChannelModeByName(param.substr(0, param.length() - 1).upper()); 702 } 703 704 if (!cm) 705 { 706 source.Reply(_("There is no such mode %s."), param.c_str()); 707 return; 708 } 709 710 if (cm->type != MODE_STATUS && cm->type != MODE_LIST) 711 { 712 source.Reply(_("Mode %s is not a status or list mode."), param.c_str()); 713 return; 714 } 715 716 if (!cm->mchar) 717 { 718 source.Reply(_("Mode %s is a virtual mode and can't be cleared."), cm->name.c_str()); 719 return; 720 } 721 722 std::vector<Anope::string> new_params; 723 new_params.push_back(params[0]); 724 new_params.push_back("SET"); 725 new_params.push_back("-" + stringify(cm->mchar)); 726 new_params.push_back("*"); 727 this->DoSet(source, ci, new_params); 728 } 729 730 public: 731 CommandCSMode(Module *creator) : Command(creator, "chanserv/mode", 2, 4) 732 { 733 this->SetDesc(_("Control modes and mode locks on a channel")); 734 this->SetSyntax(_("\037channel\037 LOCK {ADD|DEL|SET|LIST} [\037what\037]")); 735 this->SetSyntax(_("\037channel\037 SET \037modes\037")); 736 this->SetSyntax(_("\037channel\037 CLEAR [\037what\037]")); 737 } 738 739 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 740 { 741 const Anope::string &subcommand = params[1]; 742 743 ChannelInfo *ci = ChannelInfo::Find(params[0]); 744 745 if (!ci) 746 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); 747 else if (subcommand.equals_ci("LOCK") && params.size() > 2) 748 { 749 if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration")) 750 source.Reply(ACCESS_DENIED); 751 else 752 this->DoLock(source, ci, params); 753 } 754 else if (!ci->c) 755 source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str()); 756 else if (subcommand.equals_ci("SET") && params.size() > 2) 757 this->DoSet(source, ci, params); 758 else if (subcommand.equals_ci("CLEAR")) 759 { 760 if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/administration")) 761 source.Reply(ACCESS_DENIED); 762 else 763 this->DoClear(source, ci, params); 764 } 765 else 766 this->OnSyntaxError(source, ""); 767 } 768 769 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 770 { 771 this->SendSyntax(source); 772 source.Reply(" "); 773 source.Reply(_("Mainly controls mode locks and mode access (which is different from channel access)\n" 774 "on a channel.\n" 775 " \n" 776 "The \002%s LOCK\002 command allows you to add, delete, and view mode locks on a channel.\n" 777 "If a mode is locked on or off, services will not allow that mode to be changed. The \002SET\002\n" 778 "command will clear all existing mode locks and set the new one given, while \002ADD\002 and \002DEL\002\n" 779 "modify the existing mode lock.\n" 780 "Example:\n" 781 " \002MODE #channel LOCK ADD +bmnt *!*@*aol*\002\n" 782 " \n" 783 "The \002%s SET\002 command allows you to set modes through services. Wildcards * and ? may\n" 784 "be given as parameters for list and status modes.\n" 785 "Example:\n" 786 " \002MODE #channel SET +v *\002\n" 787 " Sets voice status to all users in the channel.\n" 788 " \n" 789 " \002MODE #channel SET -b ~c:*\n" 790 " Clears all extended bans that start with ~c:\n" 791 " \n" 792 "The \002%s CLEAR\002 command is an easy way to clear modes on a channel. \037what\037 may be\n" 793 "any mode name. Examples include bans, excepts, inviteoverrides, ops, halfops, and voices. If \037what\037\n" 794 "is not given then all basic modes are removed."), 795 source.command.upper().c_str(), source.command.upper().c_str(), source.command.upper().c_str()); 796 return true; 797 } 798 }; 799 800 static Anope::map<std::pair<bool, Anope::string> > modes; 801 802 class CommandCSModes : public Command 803 { 804 public: 805 CommandCSModes(Module *creator) : Command(creator, "chanserv/modes", 1, 2) 806 { 807 this->SetSyntax(_("\037channel\037 [\037user\037]")); 808 } 809 810 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 811 { 812 User *u = source.GetUser(), 813 *targ = params.size() > 1 ? User::Find(params[1], true) : u; 814 ChannelInfo *ci = ChannelInfo::Find(params[0]); 815 816 if (!targ) 817 { 818 if (params.size() > 1) 819 source.Reply(NICK_X_NOT_IN_USE, params[1].c_str()); 820 return; 821 } 822 823 if (!ci) 824 { 825 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); 826 return; 827 } 828 else if (!ci->c) 829 { 830 source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str()); 831 return; 832 } 833 834 AccessGroup u_access = source.AccessFor(ci), targ_access = ci->AccessFor(targ); 835 const std::pair<bool, Anope::string> &m = modes[source.command]; 836 837 bool can_override = source.HasPriv("chanserv/administration"); 838 bool override = false; 839 840 if (m.second.empty()) 841 { 842 source.Reply(ACCESS_DENIED); 843 return; 844 } 845 846 if (u == targ ? !u_access.HasPriv(m.second + "ME") : !u_access.HasPriv(m.second)) 847 { 848 if (!can_override) 849 { 850 source.Reply(ACCESS_DENIED); 851 return; 852 } 853 else 854 override = true; 855 } 856 857 if (!override && !m.first && u != targ && (targ->IsProtected() || (ci->HasExt("PEACE") && targ_access >= u_access))) 858 { 859 if (!can_override) 860 { 861 source.Reply(ACCESS_DENIED); 862 return; 863 } 864 else 865 override = true; 866 } 867 868 if (!ci->c->FindUser(targ)) 869 { 870 source.Reply(NICK_X_NOT_ON_CHAN, targ->nick.c_str(), ci->name.c_str()); 871 return; 872 } 873 874 if (m.first) 875 ci->c->SetMode(NULL, m.second, targ->GetUID()); 876 else 877 ci->c->RemoveMode(NULL, m.second, targ->GetUID()); 878 879 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "on " << targ->nick; 880 } 881 882 const Anope::string GetDesc(CommandSource &source) const anope_override 883 { 884 const std::pair<bool, Anope::string> &m = modes[source.command]; 885 if (!m.second.empty()) 886 { 887 if (m.first) 888 return Anope::printf(Language::Translate(source.GetAccount(), _("Gives you or the specified nick %s status on a channel")), m.second.c_str()); 889 else 890 return Anope::printf(Language::Translate(source.GetAccount(), _("Removes %s status from you or the specified nick on a channel")), m.second.c_str()); 891 } 892 else 893 return ""; 894 } 895 896 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 897 { 898 const std::pair<bool, Anope::string> &m = modes[source.command]; 899 if (m.second.empty()) 900 return false; 901 902 this->SendSyntax(source); 903 source.Reply(" "); 904 if (m.first) 905 source.Reply(_("Gives %s status to the selected nick on a channel. If \037nick\037 is\n" 906 "not given, it will %s you."), 907 m.second.upper().c_str(), m.second.lower().c_str()); 908 else 909 source.Reply(_("Removes %s status from the selected nick on a channel. If \037nick\037 is\n" 910 "not given, it will de%s you."), 911 m.second.upper().c_str(), m.second.lower().c_str()); 912 source.Reply(" "); 913 source.Reply(_("You must have the %s(ME) privilege on the channel to use this command."), m.second.upper().c_str()); 914 915 return true; 916 } 917 }; 918 919 class CSMode : public Module 920 { 921 CommandCSMode commandcsmode; 922 CommandCSModes commandcsmodes; 923 ExtensibleItem<ModeLocksImpl> modelocks; 924 Serialize::Type modelocks_type; 925 926 public: 927 CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 928 commandcsmode(this), commandcsmodes(this), 929 modelocks(this, "modelocks"), 930 modelocks_type("ModeLock", ModeLockImpl::Unserialize) 931 { 932 933 } 934 935 void OnReload(Configuration::Conf *conf) anope_override 936 { 937 modes.clear(); 938 939 for (int i = 0; i < conf->CountBlock("command"); ++i) 940 { 941 Configuration::Block *block = conf->GetBlock("command", i); 942 943 const Anope::string &cname = block->Get<const Anope::string>("name"), 944 &cmd = block->Get<const Anope::string>("command"); 945 946 if (cname.empty() || cmd != "chanserv/modes") 947 continue; 948 949 const Anope::string &set = block->Get<const Anope::string>("set"), 950 &unset = block->Get<const Anope::string>("unset"); 951 952 if (set.empty() && unset.empty()) 953 continue; 954 955 modes[cname] = std::make_pair(!set.empty(), !set.empty() ? set : unset); 956 } 957 } 958 959 void OnCheckModes(Reference<Channel> &c) anope_override 960 { 961 if (!c || !c->ci) 962 return; 963 964 ModeLocks *locks = modelocks.Get(c->ci); 965 if (locks) 966 for (ModeLocks::ModeList::const_iterator it = locks->GetMLock().begin(), it_end = locks->GetMLock().end(); it != it_end; ++it) 967 { 968 const ModeLock *ml = *it; 969 ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); 970 if (!cm) 971 continue; 972 973 if (cm->type == MODE_REGULAR) 974 { 975 if (!c->HasMode(cm->name) && ml->set) 976 c->SetMode(NULL, cm, "", false); 977 else if (c->HasMode(cm->name) && !ml->set) 978 c->RemoveMode(NULL, cm, "", false); 979 } 980 else if (cm->type == MODE_PARAM) 981 { 982 /* If the channel doesn't have the mode, or it does and it isn't set correctly */ 983 if (ml->set) 984 { 985 Anope::string param; 986 c->GetParam(cm->name, param); 987 988 if (!c->HasMode(cm->name) || (!param.empty() && !ml->param.empty() && !param.equals_cs(ml->param))) 989 c->SetMode(NULL, cm, ml->param, false); 990 } 991 else 992 { 993 if (c->HasMode(cm->name)) 994 c->RemoveMode(NULL, cm, "", false); 995 } 996 997 } 998 else if (cm->type == MODE_LIST || cm->type == MODE_STATUS) 999 { 1000 if (ml->set) 1001 c->SetMode(NULL, cm, ml->param, false); 1002 else 1003 c->RemoveMode(NULL, cm, ml->param, false); 1004 } 1005 } 1006 } 1007 1008 void OnChanRegistered(ChannelInfo *ci) anope_override 1009 { 1010 ModeLocks *ml = modelocks.Require(ci); 1011 Anope::string mlock; 1012 spacesepstream sep(Config->GetModule(this)->Get<const Anope::string>("mlock", "+nt")); 1013 if (sep.GetToken(mlock)) 1014 { 1015 bool add = true; 1016 for (unsigned i = 0; i < mlock.length(); ++i) 1017 { 1018 if (mlock[i] == '+') 1019 { 1020 add = true; 1021 continue; 1022 } 1023 1024 if (mlock[i] == '-') 1025 { 1026 add = false; 1027 continue; 1028 } 1029 1030 ChannelMode *cm = ModeManager::FindChannelModeByChar(mlock[i]); 1031 if (!cm) 1032 continue; 1033 1034 Anope::string param; 1035 if (cm->type == MODE_PARAM) 1036 { 1037 ChannelModeParam *cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm); 1038 if (add || !cmp->minus_no_arg) 1039 { 1040 sep.GetToken(param); 1041 if (param.empty() || !cmp->IsValid(param)) 1042 continue; 1043 } 1044 } 1045 else if (cm->type != MODE_REGULAR) 1046 { 1047 sep.GetToken(param); 1048 if (param.empty()) 1049 continue; 1050 } 1051 1052 ml->SetMLock(cm, add, param); 1053 } 1054 } 1055 ml->Check(); 1056 } 1057 1058 void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_hidden) anope_override 1059 { 1060 if (!show_hidden) 1061 return; 1062 1063 ModeLocks *ml = modelocks.Get(ci); 1064 if (ml) 1065 info[_("Mode lock")] = ml->GetMLockAsString(true); 1066 } 1067 }; 1068 1069 MODULE_INIT(CSMode)