anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
cs_access.cpp (28674B)
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 14 static std::map<Anope::string, int16_t, ci::less> defaultLevels; 15 16 static inline void reset_levels(ChannelInfo *ci) 17 { 18 ci->ClearLevels(); 19 for (std::map<Anope::string, int16_t, ci::less>::iterator it = defaultLevels.begin(), it_end = defaultLevels.end(); it != it_end; ++it) 20 ci->SetLevel(it->first, it->second); 21 } 22 23 class AccessChanAccess : public ChanAccess 24 { 25 public: 26 int level; 27 28 AccessChanAccess(AccessProvider *p) : ChanAccess(p), level(0) 29 { 30 } 31 32 bool HasPriv(const Anope::string &name) const anope_override 33 { 34 return this->ci->GetLevel(name) != ACCESS_INVALID && this->level >= this->ci->GetLevel(name); 35 } 36 37 Anope::string AccessSerialize() const anope_override 38 { 39 return stringify(this->level); 40 } 41 42 void AccessUnserialize(const Anope::string &data) anope_override 43 { 44 try 45 { 46 this->level = convertTo<int>(data); 47 } 48 catch (const ConvertException &) 49 { 50 } 51 } 52 53 bool operator>(const ChanAccess &other) const anope_override 54 { 55 if (this->provider != other.provider) 56 return ChanAccess::operator>(other); 57 else 58 return this->level > anope_dynamic_static_cast<const AccessChanAccess *>(&other)->level; 59 } 60 61 bool operator<(const ChanAccess &other) const anope_override 62 { 63 if (this->provider != other.provider) 64 return ChanAccess::operator<(other); 65 else 66 return this->level < anope_dynamic_static_cast<const AccessChanAccess *>(&other)->level; 67 } 68 }; 69 70 class AccessAccessProvider : public AccessProvider 71 { 72 public: 73 static AccessAccessProvider *me; 74 75 AccessAccessProvider(Module *o) : AccessProvider(o, "access/access") 76 { 77 me = this; 78 } 79 80 ChanAccess *Create() anope_override 81 { 82 return new AccessChanAccess(this); 83 } 84 }; 85 AccessAccessProvider* AccessAccessProvider::me; 86 87 class CommandCSAccess : public Command 88 { 89 void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 90 { 91 Anope::string mask = params[2]; 92 Privilege *p = NULL; 93 int level = ACCESS_INVALID; 94 95 try 96 { 97 level = convertTo<int>(params[3]); 98 } 99 catch (const ConvertException &) 100 { 101 p = PrivilegeManager::FindPrivilege(params[3]); 102 if (p != NULL && defaultLevels[p->name]) 103 level = defaultLevels[p->name]; 104 } 105 106 if (!level) 107 { 108 source.Reply(_("Access level must be non-zero.")); 109 return; 110 } 111 else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) 112 { 113 source.Reply(CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1); 114 return; 115 } 116 117 AccessGroup u_access = source.AccessFor(ci); 118 const ChanAccess *highest = u_access.Highest(); 119 120 AccessChanAccess tmp_access(AccessAccessProvider::me); 121 tmp_access.ci = ci; 122 tmp_access.level = level; 123 124 bool override = false; 125 const NickAlias *na = NULL; 126 127 if ((!highest || *highest <= tmp_access) && !u_access.founder) 128 { 129 if (source.HasPriv("chanserv/access/modify")) 130 override = true; 131 else 132 { 133 source.Reply(ACCESS_DENIED); 134 return; 135 } 136 } 137 138 if (IRCD->IsChannelValid(mask)) 139 { 140 if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access")) 141 { 142 source.Reply(_("Channels may not be on access lists.")); 143 return; 144 } 145 146 ChannelInfo *targ_ci = ChannelInfo::Find(mask); 147 if (targ_ci == NULL) 148 { 149 source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str()); 150 return; 151 } 152 else if (ci == targ_ci) 153 { 154 source.Reply(_("You can't add a channel to its own access list.")); 155 return; 156 } 157 158 mask = targ_ci->name; 159 } 160 else 161 { 162 na = NickAlias::Find(mask); 163 164 if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access")) 165 { 166 source.Reply(_("Masks and unregistered users may not be on access lists.")); 167 return; 168 } 169 else if (mask.find_first_of("!*@") == Anope::string::npos && !na) 170 { 171 User *targ = User::Find(mask, true); 172 if (targ != NULL) 173 mask = "*!*@" + targ->GetDisplayedHost(); 174 else 175 { 176 source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); 177 return; 178 } 179 } 180 181 if (na) 182 mask = na->nick; 183 } 184 185 for (unsigned i = ci->GetAccessCount(); i > 0; --i) 186 { 187 const ChanAccess *access = ci->GetAccess(i - 1); 188 if ((na && na->nc == access->GetAccount()) || mask.equals_ci(access->Mask())) 189 { 190 /* Don't allow lowering from a level >= u_level */ 191 if ((!highest || *access >= *highest) && !u_access.founder && !source.HasPriv("chanserv/access/modify")) 192 { 193 source.Reply(ACCESS_DENIED); 194 return; 195 } 196 delete ci->EraseAccess(i - 1); 197 break; 198 } 199 } 200 201 unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024"); 202 if (access_max && ci->GetDeepAccessCount() >= access_max) 203 { 204 source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max); 205 return; 206 } 207 208 ServiceReference<AccessProvider> provider("AccessProvider", "access/access"); 209 if (!provider) 210 return; 211 AccessChanAccess *access = anope_dynamic_static_cast<AccessChanAccess *>(provider->Create()); 212 access->SetMask(mask, ci); 213 access->creator = source.GetNick(); 214 access->level = level; 215 access->last_seen = 0; 216 access->created = Anope::CurTime; 217 ci->AddAccess(access); 218 219 FOREACH_MOD(OnAccessAdd, (ci, source, access)); 220 221 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << " with level " << level; 222 if (p != NULL) 223 source.Reply(_("\002%s\002 added to %s access list at privilege %s (level %d)"), access->Mask().c_str(), ci->name.c_str(), p->name.c_str(), level); 224 else 225 source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->Mask().c_str(), ci->name.c_str(), level); 226 } 227 228 void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 229 { 230 Anope::string mask = params[2]; 231 232 if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickAlias::Find(mask)) 233 { 234 User *targ = User::Find(mask, true); 235 if (targ != NULL) 236 mask = "*!*@" + targ->GetDisplayedHost(); 237 else 238 { 239 source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); 240 return; 241 } 242 } 243 244 if (!ci->GetAccessCount()) 245 source.Reply(_("%s access list is empty."), ci->name.c_str()); 246 else if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) 247 { 248 class AccessDelCallback : public NumberList 249 { 250 CommandSource &source; 251 ChannelInfo *ci; 252 Command *c; 253 unsigned deleted; 254 Anope::string Nicks; 255 bool denied; 256 bool override; 257 public: 258 AccessDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), deleted(0), denied(false), override(false) 259 { 260 if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && source.HasPriv("chanserv/access/modify")) 261 this->override = true; 262 } 263 264 ~AccessDelCallback() 265 { 266 if (denied && !deleted) 267 source.Reply(ACCESS_DENIED); 268 else if (!deleted) 269 source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); 270 else 271 { 272 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "to delete " << Nicks; 273 274 if (deleted == 1) 275 source.Reply(_("Deleted 1 entry from %s access list."), ci->name.c_str()); 276 else 277 source.Reply(_("Deleted %d entries from %s access list."), deleted, ci->name.c_str()); 278 } 279 } 280 281 void HandleNumber(unsigned Number) anope_override 282 { 283 if (!Number || Number > ci->GetAccessCount()) 284 return; 285 286 ChanAccess *access = ci->GetAccess(Number - 1); 287 288 AccessGroup ag = source.AccessFor(ci); 289 const ChanAccess *u_highest = ag.Highest(); 290 291 if ((!u_highest || *u_highest <= *access) && !ag.founder && !this->override && access->GetAccount() != source.nc) 292 { 293 denied = true; 294 return; 295 } 296 297 ++deleted; 298 if (!Nicks.empty()) 299 Nicks += ", " + access->Mask(); 300 else 301 Nicks = access->Mask(); 302 303 ci->EraseAccess(Number - 1); 304 305 FOREACH_MOD(OnAccessDel, (ci, source, access)); 306 delete access; 307 } 308 } 309 delcallback(source, ci, this, mask); 310 delcallback.Process(); 311 } 312 else 313 { 314 AccessGroup u_access = source.AccessFor(ci); 315 const ChanAccess *highest = u_access.Highest(); 316 317 for (unsigned i = ci->GetAccessCount(); i > 0; --i) 318 { 319 ChanAccess *access = ci->GetAccess(i - 1); 320 if (mask.equals_ci(access->Mask())) 321 { 322 if (access->GetAccount() != source.nc && !u_access.founder && (!highest || *highest <= *access) && !source.HasPriv("chanserv/access/modify")) 323 source.Reply(ACCESS_DENIED); 324 else 325 { 326 source.Reply(_("\002%s\002 deleted from %s access list."), access->Mask().c_str(), ci->name.c_str()); 327 bool override = !u_access.founder && !u_access.HasPriv("ACCESS_CHANGE") && access->GetAccount() != source.nc; 328 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->Mask(); 329 330 ci->EraseAccess(i - 1); 331 FOREACH_MOD(OnAccessDel, (ci, source, access)); 332 delete access; 333 } 334 return; 335 } 336 } 337 338 source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str()); 339 } 340 341 return; 342 } 343 344 void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, ListFormatter &list) 345 { 346 const Anope::string &nick = params.size() > 2 ? params[2] : ""; 347 348 if (!ci->GetAccessCount()) 349 source.Reply(_("%s access list is empty."), ci->name.c_str()); 350 else if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos) 351 { 352 class AccessListCallback : public NumberList 353 { 354 ListFormatter &list; 355 ChannelInfo *ci; 356 357 public: 358 AccessListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci) 359 { 360 } 361 362 void HandleNumber(unsigned number) anope_override 363 { 364 if (!number || number > ci->GetAccessCount()) 365 return; 366 367 const ChanAccess *access = ci->GetAccess(number - 1); 368 369 Anope::string timebuf; 370 if (ci->c) 371 for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) 372 { 373 ChannelInfo *p; 374 if (access->Matches(cit->second->user, cit->second->user->Account(), p)) 375 timebuf = "Now"; 376 } 377 if (timebuf.empty()) 378 { 379 if (access->last_seen == 0) 380 timebuf = "Never"; 381 else 382 timebuf = Anope::strftime(access->last_seen, NULL, true); 383 } 384 385 ListFormatter::ListEntry entry; 386 entry["Number"] = stringify(number); 387 entry["Level"] = access->AccessSerialize(); 388 entry["Mask"] = access->Mask(); 389 entry["By"] = access->creator; 390 entry["Last seen"] = timebuf; 391 this->list.AddEntry(entry); 392 } 393 } 394 nl_list(list, ci, nick); 395 nl_list.Process(); 396 } 397 else 398 { 399 for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) 400 { 401 const ChanAccess *access = ci->GetAccess(i); 402 403 if (!nick.empty() && !Anope::Match(access->Mask(), nick)) 404 continue; 405 406 Anope::string timebuf; 407 if (ci->c) 408 for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) 409 { 410 ChannelInfo *p; 411 if (access->Matches(cit->second->user, cit->second->user->Account(), p)) 412 timebuf = "Now"; 413 } 414 if (timebuf.empty()) 415 { 416 if (access->last_seen == 0) 417 timebuf = "Never"; 418 else 419 timebuf = Anope::strftime(access->last_seen, NULL, true); 420 } 421 422 ListFormatter::ListEntry entry; 423 entry["Number"] = stringify(i + 1); 424 entry["Level"] = access->AccessSerialize(); 425 entry["Mask"] = access->Mask(); 426 entry["By"] = access->creator; 427 entry["Last seen"] = timebuf; 428 list.AddEntry(entry); 429 } 430 } 431 432 if (list.IsEmpty()) 433 source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); 434 else 435 { 436 std::vector<Anope::string> replies; 437 list.Process(replies); 438 439 source.Reply(_("Access list for %s:"), ci->name.c_str()); 440 441 for (unsigned i = 0; i < replies.size(); ++i) 442 source.Reply(replies[i]); 443 444 source.Reply(_("End of access list")); 445 } 446 447 return; 448 } 449 450 void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 451 { 452 if (!ci->GetAccessCount()) 453 { 454 source.Reply(_("%s access list is empty."), ci->name.c_str()); 455 return; 456 } 457 458 ListFormatter list(source.GetAccount()); 459 list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")); 460 this->ProcessList(source, ci, params, list); 461 } 462 463 void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 464 { 465 if (!ci->GetAccessCount()) 466 { 467 source.Reply(_("%s access list is empty."), ci->name.c_str()); 468 return; 469 } 470 471 ListFormatter list(source.GetAccount()); 472 list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Last seen")); 473 this->ProcessList(source, ci, params, list); 474 } 475 476 void DoClear(CommandSource &source, ChannelInfo *ci) 477 { 478 if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify")) 479 source.Reply(ACCESS_DENIED); 480 else 481 { 482 FOREACH_MOD(OnAccessClear, (ci, source)); 483 484 ci->ClearAccess(); 485 486 source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str()); 487 488 bool override = !source.IsFounder(ci); 489 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list"; 490 } 491 492 return; 493 } 494 495 public: 496 CommandCSAccess(Module *creator) : Command(creator, "chanserv/access", 2, 4) 497 { 498 this->SetDesc(_("Modify the list of privileged users")); 499 this->SetSyntax(_("\037channel\037 ADD \037mask\037 \037level\037")); 500 this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}")); 501 this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]")); 502 this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037list\037]")); 503 this->SetSyntax(_("\037channel\037 CLEAR")); 504 } 505 506 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 507 { 508 const Anope::string &cmd = params[1]; 509 const Anope::string &nick = params.size() > 2 ? params[2] : ""; 510 const Anope::string &s = params.size() > 3 ? params[3] : ""; 511 512 ChannelInfo *ci = ChannelInfo::Find(params[0]); 513 if (ci == NULL) 514 { 515 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); 516 return; 517 } 518 519 bool is_list = cmd.equals_ci("LIST") || cmd.equals_ci("VIEW"); 520 bool is_clear = cmd.equals_ci("CLEAR"); 521 bool is_del = cmd.equals_ci("DEL"); 522 523 bool has_access = false; 524 if (source.HasPriv("chanserv/access/modify")) 525 has_access = true; 526 else if (is_list && source.HasPriv("chanserv/access/list")) 527 has_access = true; 528 else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST")) 529 has_access = true; 530 else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE")) 531 has_access = true; 532 else if (is_del) 533 { 534 const NickAlias *na = NickAlias::Find(nick); 535 if (na && na->nc == source.GetAccount()) 536 has_access = true; 537 } 538 539 /* If LIST, we don't *require* any parameters, but we can take any. 540 * If DEL, we require a nick and no level. 541 * Else (ADD), we require a level (which implies a nick). */ 542 if (is_list || is_clear ? 0 : (cmd.equals_ci("DEL") ? (nick.empty() || !s.empty()) : s.empty())) 543 this->OnSyntaxError(source, cmd); 544 else if (!has_access) 545 source.Reply(ACCESS_DENIED); 546 else if (Anope::ReadOnly && !is_list) 547 source.Reply(_("Sorry, channel access list modification is temporarily disabled.")); 548 else if (cmd.equals_ci("ADD")) 549 this->DoAdd(source, ci, params); 550 else if (cmd.equals_ci("DEL")) 551 this->DoDel(source, ci, params); 552 else if (cmd.equals_ci("LIST")) 553 this->DoList(source, ci, params); 554 else if (cmd.equals_ci("VIEW")) 555 this->DoView(source, ci, params); 556 else if (cmd.equals_ci("CLEAR")) 557 this->DoClear(source, ci); 558 else 559 this->OnSyntaxError(source, ""); 560 561 return; 562 } 563 564 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 565 { 566 this->SendSyntax(source); 567 source.Reply(" "); 568 source.Reply(_("Maintains the \002access list\002 for a channel. The access\n" 569 "list specifies which users are allowed chanop status or\n" 570 "access to %s commands on the channel. Different\n" 571 "user levels allow for access to different subsets of\n" 572 "privileges. Any registered user not on the access list has\n" 573 "a user level of 0, and any unregistered user has a user level\n" 574 "of -1."), source.service->nick.c_str()); 575 source.Reply(" "); 576 source.Reply(_("The \002ACCESS ADD\002 command adds the given mask to the\n" 577 "access list with the given user level; if the mask is\n" 578 "already present on the list, its access level is changed to\n" 579 "the level specified in the command. The \037level\037 specified\n" 580 "may be a numerical level or the name of a privilege (eg AUTOOP).\n" 581 "When a user joins the channel the access they receive is from the\n" 582 "highest level entry in the access list.")); 583 if (!Config->GetModule("chanserv")->Get<bool>("disallow_channel_access")) 584 source.Reply(_("The given mask may also be a channel, which will use the\n" 585 "access list from the other channel up to the given \037level\037.")); 586 source.Reply(" "); 587 source.Reply(_("The \002ACCESS DEL\002 command removes the given nick from the\n" 588 "access list. If a list of entry numbers is given, those\n" 589 "entries are deleted. (See the example for LIST below.)\n" 590 "You may remove yourself from an access list, even if you\n" 591 "do not have access to modify that list otherwise.")); 592 source.Reply(" "); 593 source.Reply(_("The \002ACCESS LIST\002 command displays the access list. If\n" 594 "a wildcard mask is given, only those entries matching the\n" 595 "mask are displayed. If a list of entry numbers is given,\n" 596 "only those entries are shown; for example:\n" 597 " \002ACCESS #channel LIST 2-5,7-9\002\n" 598 " Lists access entries numbered 2 through 5 and\n" 599 " 7 through 9.\n" 600 " \n" 601 "The \002ACCESS VIEW\002 command displays the access list similar\n" 602 "to \002ACCESS LIST\002 but shows the creator and last used time.\n" 603 " \n" 604 "The \002ACCESS CLEAR\002 command clears all entries of the\n" 605 "access list.")); 606 source.Reply(" "); 607 608 BotInfo *bi; 609 Anope::string cmd; 610 if (Command::FindCommandFromService("chanserv/levels", bi, cmd)) 611 source.Reply(_("\002User access levels\002 can be seen by using the\n" 612 "\002%s\002 command; type \002%s%s HELP LEVELS\002 for\n" 613 "information."), cmd.c_str(), Config->StrictPrivmsg.c_str(), bi->nick.c_str()); 614 return true; 615 } 616 }; 617 618 class CommandCSLevels : public Command 619 { 620 void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 621 { 622 const Anope::string &what = params[2]; 623 const Anope::string &lev = params[3]; 624 625 int level; 626 627 if (lev.equals_ci("FOUNDER")) 628 level = ACCESS_FOUNDER; 629 else 630 { 631 try 632 { 633 level = convertTo<int>(lev); 634 } 635 catch (const ConvertException &) 636 { 637 this->OnSyntaxError(source, "SET"); 638 return; 639 } 640 } 641 642 if (level <= ACCESS_INVALID || level > ACCESS_FOUNDER) 643 source.Reply(_("Level must be between %d and %d inclusive."), ACCESS_INVALID + 1, ACCESS_FOUNDER - 1); 644 else 645 { 646 Privilege *p = PrivilegeManager::FindPrivilege(what); 647 if (p == NULL) 648 source.Reply(_("Setting \002%s\002 not known. Type \002%s%s HELP LEVELS\002 for a list of valid settings."), what.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str()); 649 else 650 { 651 bool override = !source.AccessFor(ci).HasPriv("FOUNDER"); 652 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << p->name << " to level " << level; 653 654 ci->SetLevel(p->name, level); 655 FOREACH_MOD(OnLevelChange, (source, ci, p->name, level)); 656 657 if (level == ACCESS_FOUNDER) 658 source.Reply(_("Level for %s on channel %s changed to founder only."), p->name.c_str(), ci->name.c_str()); 659 else 660 source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), p->name.c_str(), ci->name.c_str(), level); 661 } 662 } 663 } 664 665 void DoDisable(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 666 { 667 const Anope::string &what = params[2]; 668 669 /* Don't allow disabling of the founder level. It would be hard to change it back if you don't have access to use this command */ 670 if (what.equals_ci("FOUNDER")) 671 { 672 source.Reply(_("You can not disable the founder privilege because it would be impossible to reenable it at a later time.")); 673 return; 674 } 675 676 Privilege *p = PrivilegeManager::FindPrivilege(what); 677 if (p != NULL) 678 { 679 bool override = !source.AccessFor(ci).HasPriv("FOUNDER"); 680 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable " << p->name; 681 682 ci->SetLevel(p->name, ACCESS_INVALID); 683 FOREACH_MOD(OnLevelChange, (source, ci, p->name, ACCESS_INVALID)); 684 685 source.Reply(_("\002%s\002 disabled on channel %s."), p->name.c_str(), ci->name.c_str()); 686 return; 687 } 688 689 source.Reply(_("Setting \002%s\002 not known. Type \002%s%s HELP LEVELS\002 for a list of valid settings."), what.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str()); 690 } 691 692 void DoList(CommandSource &source, ChannelInfo *ci) 693 { 694 source.Reply(_("Access level settings for channel %s:"), ci->name.c_str()); 695 696 ListFormatter list(source.GetAccount()); 697 list.AddColumn(_("Name")).AddColumn(_("Level")); 698 699 const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges(); 700 701 for (unsigned i = 0; i < privs.size(); ++i) 702 { 703 const Privilege &p = privs[i]; 704 int16_t j = ci->GetLevel(p.name); 705 706 ListFormatter::ListEntry entry; 707 entry["Name"] = p.name; 708 709 if (j == ACCESS_INVALID) 710 entry["Level"] = Language::Translate(source.GetAccount(), _("(disabled)")); 711 else if (j == ACCESS_FOUNDER) 712 entry["Level"] = Language::Translate(source.GetAccount(), _("(founder only)")); 713 else 714 entry["Level"] = stringify(j); 715 716 list.AddEntry(entry); 717 } 718 719 std::vector<Anope::string> replies; 720 list.Process(replies); 721 722 for (unsigned i = 0; i < replies.size(); ++i) 723 source.Reply(replies[i]); 724 } 725 726 void DoReset(CommandSource &source, ChannelInfo *ci) 727 { 728 bool override = !source.AccessFor(ci).HasPriv("FOUNDER"); 729 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to reset all levels"; 730 731 reset_levels(ci); 732 FOREACH_MOD(OnLevelChange, (source, ci, "ALL", 0)); 733 734 source.Reply(_("Access levels for \002%s\002 reset to defaults."), ci->name.c_str()); 735 return; 736 } 737 738 public: 739 CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4) 740 { 741 this->SetDesc(_("Redefine the meanings of access levels")); 742 this->SetSyntax(_("\037channel\037 SET \037type\037 \037level\037")); 743 this->SetSyntax(_("\037channel\037 {DIS | DISABLE} \037type\037")); 744 this->SetSyntax(_("\037channel\037 LIST")); 745 this->SetSyntax(_("\037channel\037 RESET")); 746 } 747 748 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 749 { 750 const Anope::string &cmd = params[1]; 751 const Anope::string &what = params.size() > 2 ? params[2] : ""; 752 const Anope::string &s = params.size() > 3 ? params[3] : ""; 753 754 ChannelInfo *ci = ChannelInfo::Find(params[0]); 755 if (ci == NULL) 756 { 757 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); 758 return; 759 } 760 761 bool has_access = false; 762 if (source.HasPriv("chanserv/access/modify")) 763 has_access = true; 764 else if (cmd.equals_ci("LIST") && source.HasPriv("chanserv/access/list")) 765 has_access = true; 766 else if (source.AccessFor(ci).HasPriv("FOUNDER")) 767 has_access = true; 768 769 /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only 770 * one; else, we want none. 771 */ 772 if (cmd.equals_ci("SET") ? s.empty() : (cmd.substr(0, 3).equals_ci("DIS") ? (what.empty() || !s.empty()) : !what.empty())) 773 this->OnSyntaxError(source, cmd); 774 else if (!has_access) 775 source.Reply(ACCESS_DENIED); 776 else if (Anope::ReadOnly && !cmd.equals_ci("LIST")) 777 source.Reply(READ_ONLY_MODE); 778 else if (cmd.equals_ci("SET")) 779 this->DoSet(source, ci, params); 780 else if (cmd.equals_ci("DIS") || cmd.equals_ci("DISABLE")) 781 this->DoDisable(source, ci, params); 782 else if (cmd.equals_ci("LIST")) 783 this->DoList(source, ci); 784 else if (cmd.equals_ci("RESET")) 785 this->DoReset(source, ci); 786 else 787 this->OnSyntaxError(source, ""); 788 789 return; 790 } 791 792 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 793 { 794 if (subcommand.equals_ci("DESC")) 795 { 796 source.Reply(_("The following feature/function names are available:")); 797 798 ListFormatter list(source.GetAccount()); 799 list.AddColumn(_("Name")).AddColumn(_("Description")); 800 801 const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges(); 802 for (unsigned i = 0; i < privs.size(); ++i) 803 { 804 const Privilege &p = privs[i]; 805 ListFormatter::ListEntry entry; 806 entry["Name"] = p.name; 807 entry["Description"] = Language::Translate(source.nc, p.desc.c_str()); 808 list.AddEntry(entry); 809 } 810 811 std::vector<Anope::string> replies; 812 list.Process(replies); 813 814 for (unsigned i = 0; i < replies.size(); ++i) 815 source.Reply(replies[i]); 816 } 817 else 818 { 819 this->SendSyntax(source); 820 source.Reply(" "); 821 source.Reply(_("The \002LEVELS\002 command allows fine control over the meaning of\n" 822 "the numeric access levels used for channels. With this\n" 823 "command, you can define the access level required for most\n" 824 "of %s's functions. (The \002SET FOUNDER\002 and this command\n" 825 "are always restricted to the channel founder.)\n" 826 " \n" 827 "\002LEVELS SET\002 allows the access level for a function or group of\n" 828 "functions to be changed. \002LEVELS DISABLE\002 (or \002DIS\002 for short)\n" 829 "disables an automatic feature or disallows access to a\n" 830 "function by anyone, INCLUDING the founder (although, the founder\n" 831 "can always reenable it). Use \002LEVELS SET founder\002 to make a level\n" 832 "founder only.\n" 833 " \n" 834 "\002LEVELS LIST\002 shows the current levels for each function or\n" 835 "group of functions. \002LEVELS RESET\002 resets the levels to the\n" 836 "default levels of a newly-created channel.\n" 837 " \n" 838 "For a list of the features and functions whose levels can be\n" 839 "set, see \002HELP LEVELS DESC\002."), source.service->nick.c_str()); 840 } 841 return true; 842 } 843 }; 844 845 class CSAccess : public Module 846 { 847 AccessAccessProvider accessprovider; 848 CommandCSAccess commandcsaccess; 849 CommandCSLevels commandcslevels; 850 851 public: 852 CSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 853 accessprovider(this), commandcsaccess(this), commandcslevels(this) 854 { 855 this->SetPermanent(true); 856 857 } 858 859 void OnReload(Configuration::Conf *conf) anope_override 860 { 861 defaultLevels.clear(); 862 863 for (int i = 0; i < conf->CountBlock("privilege"); ++i) 864 { 865 Configuration::Block *priv = conf->GetBlock("privilege", i); 866 867 const Anope::string &pname = priv->Get<const Anope::string>("name"); 868 869 Privilege *p = PrivilegeManager::FindPrivilege(pname); 870 if (p == NULL) 871 continue; 872 873 const Anope::string &value = priv->Get<const Anope::string>("level"); 874 if (value.empty()) 875 continue; 876 else if (value.equals_ci("founder")) 877 defaultLevels[p->name] = ACCESS_FOUNDER; 878 else if (value.equals_ci("disabled")) 879 defaultLevels[p->name] = ACCESS_INVALID; 880 else 881 defaultLevels[p->name] = priv->Get<int16_t>("level"); 882 } 883 } 884 885 void OnCreateChan(ChannelInfo *ci) anope_override 886 { 887 reset_levels(ci); 888 } 889 890 EventReturn OnGroupCheckPriv(const AccessGroup *group, const Anope::string &priv) anope_override 891 { 892 if (group->ci == NULL) 893 return EVENT_CONTINUE; 894 895 const ChanAccess *highest = group->Highest(); 896 if (highest && highest->provider == &accessprovider) 897 { 898 /* Access accessprovider is the only accessprovider with the concept of negative access, 899 * so check they don't have negative access 900 */ 901 const AccessChanAccess *aca = anope_dynamic_static_cast<const AccessChanAccess *>(highest); 902 903 if (aca->level < 0) 904 return EVENT_CONTINUE; 905 } 906 907 /* Special case. Allows a level of -1 to match anyone, and a level of 0 to match anyone identified. */ 908 int16_t level = group->ci->GetLevel(priv); 909 if (level == -1) 910 return EVENT_ALLOW; 911 else if (level == 0 && group->nc && !group->nc->HasExt("UNCONFIRMED")) 912 return EVENT_ALLOW; 913 return EVENT_CONTINUE; 914 } 915 }; 916 917 MODULE_INIT(CSAccess)