00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "module.h"
00015
00016 static std::map<Anope::string, int16_t, ci::less> defaultLevels;
00017
00018 static inline void reset_levels(ChannelInfo *ci)
00019 {
00020 ci->ClearLevels();
00021 for (std::map<Anope::string, int16_t, ci::less>::iterator it = defaultLevels.begin(), it_end = defaultLevels.end(); it != it_end; ++it)
00022 ci->SetLevel(it->first, it->second);
00023 }
00024
00025 class AccessChanAccess : public ChanAccess
00026 {
00027 public:
00028 int level;
00029
00030 AccessChanAccess(AccessProvider *p) : ChanAccess(p)
00031 {
00032 }
00033
00034 bool HasPriv(const Anope::string &name) const anope_override
00035 {
00036 return this->ci->GetLevel(name) != ACCESS_INVALID && this->level >= this->ci->GetLevel(name);
00037 }
00038
00039 Anope::string AccessSerialize() const
00040 {
00041 return stringify(this->level);
00042 }
00043
00044 void AccessUnserialize(const Anope::string &data) anope_override
00045 {
00046 this->level = convertTo<int>(data);
00047 }
00048
00049 static int DetermineLevel(const ChanAccess *access)
00050 {
00051 if (access->provider->name == "access/access")
00052 {
00053 const AccessChanAccess *aaccess = anope_dynamic_static_cast<const AccessChanAccess *>(access);
00054 return aaccess->level;
00055 }
00056 else
00057 {
00058 int highest = 1;
00059 const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
00060
00061 for (unsigned i = 0; i < privs.size(); ++i)
00062 {
00063 const Privilege &p = privs[i];
00064 if (access->ci->GetLevel(p.name) > highest && access->HasPriv(p.name))
00065 highest = access->ci->GetLevel(p.name);
00066 }
00067
00068 if (highest >= ACCESS_FOUNDER)
00069 highest = ACCESS_FOUNDER - 1;
00070
00071 return highest;
00072 }
00073 }
00074 };
00075
00076 class AccessAccessProvider : public AccessProvider
00077 {
00078 public:
00079 AccessAccessProvider(Module *o) : AccessProvider(o, "access/access")
00080 {
00081 }
00082
00083 ChanAccess *Create() anope_override
00084 {
00085 return new AccessChanAccess(this);
00086 }
00087 };
00088
00089 class CommandCSAccess : public Command
00090 {
00091 void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00092 {
00093 Anope::string mask = params[2];
00094 int level = ACCESS_INVALID;
00095
00096 try
00097 {
00098 level = convertTo<int>(params[3]);
00099 }
00100 catch (const ConvertException &) { }
00101
00102 if (!level)
00103 {
00104 source.Reply(_("Access level must be non-zero."));
00105 return;
00106 }
00107 else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER)
00108 {
00109 source.Reply(CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
00110 return;
00111 }
00112
00113 AccessGroup u_access = source.AccessFor(ci);
00114 const ChanAccess *highest = u_access.Highest();
00115
00116 AccessChanAccess tmp_access(NULL);
00117 tmp_access.ci = ci;
00118 tmp_access.level = level;
00119
00120 bool override = false;
00121
00122 if ((!highest || *highest <= tmp_access) && !u_access.founder)
00123 {
00124 if (source.HasPriv("chanserv/access/modify"))
00125 override = true;
00126 else
00127 {
00128 source.Reply(ACCESS_DENIED);
00129 return;
00130 }
00131 }
00132
00133 if (mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask))
00134 {
00135 User *targ = User::Find(mask, true);
00136 if (targ != NULL)
00137 mask = "*!*@" + targ->GetDisplayedHost();
00138 else
00139 {
00140 source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
00141 return;
00142 }
00143 }
00144
00145 for (unsigned i = ci->GetAccessCount(); i > 0; --i)
00146 {
00147 const ChanAccess *access = ci->GetAccess(i - 1);
00148 if (mask.equals_ci(access->mask))
00149 {
00150
00151 if ((!highest || *access >= *highest) && !u_access.founder && !source.HasPriv("chanserv/access/modify"))
00152 {
00153 source.Reply(ACCESS_DENIED);
00154 return;
00155 }
00156 ci->EraseAccess(i - 1);
00157 break;
00158 }
00159 }
00160
00161 if (ci->GetAccessCount() >= Config->CSAccessMax)
00162 {
00163 source.Reply(_("Sorry, you can only have %d access entries on a channel."), Config->CSAccessMax);
00164 return;
00165 }
00166
00167 ServiceReference<AccessProvider> provider("AccessProvider", "access/access");
00168 if (!provider)
00169 return;
00170 AccessChanAccess *access = anope_dynamic_static_cast<AccessChanAccess *>(provider->Create());
00171 access->ci = ci;
00172 access->mask = mask;
00173 access->creator = source.GetNick();
00174 access->level = level;
00175 access->last_seen = 0;
00176 access->created = Anope::CurTime;
00177 ci->AddAccess(access);
00178
00179 FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, source, access));
00180
00181 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << " with level " << level;
00182 source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->mask.c_str(), ci->name.c_str(), level);
00183
00184 return;
00185 }
00186
00187 void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00188 {
00189 Anope::string mask = params[2];
00190
00191 if (!isdigit(mask[0]) && mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask))
00192 {
00193 User *targ = User::Find(mask, true);
00194 if (targ != NULL)
00195 mask = "*!*@" + targ->GetDisplayedHost();
00196 else
00197 {
00198 source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
00199 return;
00200 }
00201 }
00202
00203 if (!ci->GetAccessCount())
00204 source.Reply(_("%s access list is empty."), ci->name.c_str());
00205 else if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
00206 {
00207 class AccessDelCallback : public NumberList
00208 {
00209 CommandSource &source;
00210 ChannelInfo *ci;
00211 Command *c;
00212 unsigned deleted;
00213 Anope::string Nicks;
00214 bool Denied;
00215 bool override;
00216 public:
00217 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)
00218 {
00219 if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && source.HasPriv("chanserv/access/modify"))
00220 this->override = true;
00221 }
00222
00223 ~AccessDelCallback()
00224 {
00225 if (Denied && !deleted)
00226 source.Reply(ACCESS_DENIED);
00227 else if (!deleted)
00228 source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
00229 else
00230 {
00231 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "to delete " << Nicks;
00232
00233 if (deleted == 1)
00234 source.Reply(_("Deleted 1 entry from %s access list."), ci->name.c_str());
00235 else
00236 source.Reply(_("Deleted %d entries from %s access list."), deleted, ci->name.c_str());
00237 }
00238 }
00239
00240 void HandleNumber(unsigned Number) anope_override
00241 {
00242 if (!Number || Number > ci->GetAccessCount())
00243 return;
00244
00245 ChanAccess *access = ci->GetAccess(Number - 1);
00246
00247 AccessGroup u_access = source.AccessFor(ci);
00248 const ChanAccess *u_highest = u_access.Highest();
00249
00250 if ((!u_highest || *u_highest <= *access) && !u_access.founder && !this->override && !access->mask.equals_ci(source.nc->display))
00251 {
00252 Denied = true;
00253 return;
00254 }
00255
00256 ++deleted;
00257 if (!Nicks.empty())
00258 Nicks += ", " + access->mask;
00259 else
00260 Nicks = access->mask;
00261
00262 FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, source, access));
00263
00264 ci->EraseAccess(Number - 1);
00265 }
00266 }
00267 delcallback(source, ci, this, mask);
00268 delcallback.Process();
00269 }
00270 else
00271 {
00272 AccessGroup u_access = source.AccessFor(ci);
00273 const ChanAccess *highest = u_access.Highest();
00274
00275 for (unsigned i = ci->GetAccessCount(); i > 0; --i)
00276 {
00277 ChanAccess *access = ci->GetAccess(i - 1);
00278 if (mask.equals_ci(access->mask))
00279 {
00280 if (!access->mask.equals_ci(source.nc->display) && !u_access.founder && (!highest || *highest <= *access) && !source.HasPriv("chanserv/access/modify"))
00281 source.Reply(ACCESS_DENIED);
00282 else
00283 {
00284 source.Reply(_("\002%s\002 deleted from %s access list."), access->mask.c_str(), ci->name.c_str());
00285 bool override = !u_access.founder && !u_access.HasPriv("ACCESS_CHANGE") && !access->mask.equals_ci(source.nc->display);
00286 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->mask;
00287
00288 FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, source, access));
00289 delete access;
00290 }
00291 return;
00292 }
00293 }
00294
00295 source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
00296 }
00297
00298 return;
00299 }
00300
00301 void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms, ListFormatter &list)
00302 {
00303 const Anope::string &nick = params.size() > 2 ? params[2] : "";
00304
00305 if (!ci->GetAccessCount())
00306 source.Reply(_("%s access list is empty."), ci->name.c_str());
00307 else if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
00308 {
00309 class AccessListCallback : public NumberList
00310 {
00311 ListFormatter &list;
00312 ChannelInfo *ci;
00313
00314 public:
00315 AccessListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
00316 {
00317 }
00318
00319 void HandleNumber(unsigned number) anope_override
00320 {
00321 if (!number || number > ci->GetAccessCount())
00322 return;
00323
00324 const ChanAccess *access = ci->GetAccess(number - 1);
00325
00326 Anope::string timebuf;
00327 if (ci->c)
00328 for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
00329 if (access->Matches((*cit)->user, (*cit)->user->Account()))
00330 timebuf = "Now";
00331 if (timebuf.empty())
00332 {
00333 if (access->last_seen == 0)
00334 timebuf = "Never";
00335 else
00336 timebuf = Anope::strftime(access->last_seen, NULL, true);
00337 }
00338
00339 ListFormatter::ListEntry entry;
00340 entry["Number"] = stringify(number);
00341 entry["Level"] = stringify(AccessChanAccess::DetermineLevel(access));
00342 entry["Mask"] = access->mask;
00343 entry["By"] = access->creator;
00344 entry["Last seen"] = timebuf;
00345 this->list.AddEntry(entry);
00346 }
00347 }
00348 nl_list(list, ci, nick);
00349 nl_list.Process();
00350 }
00351 else
00352 {
00353 for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
00354 {
00355 const ChanAccess *access = ci->GetAccess(i);
00356
00357 if (!nick.empty() && !Anope::Match(access->mask, nick))
00358 continue;
00359
00360 Anope::string timebuf;
00361 if (ci->c)
00362 for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
00363 if (access->Matches((*cit)->user, (*cit)->user->Account()))
00364 timebuf = "Now";
00365 if (timebuf.empty())
00366 {
00367 if (access->last_seen == 0)
00368 timebuf = "Never";
00369 else
00370 timebuf = Anope::strftime(access->last_seen, NULL, true);
00371 }
00372
00373 ListFormatter::ListEntry entry;
00374 entry["Number"] = stringify(i + 1);
00375 entry["Level"] = stringify(AccessChanAccess::DetermineLevel(access));
00376 entry["Mask"] = access->mask;
00377 entry["By"] = access->creator;
00378 entry["Last seen"] = timebuf;
00379 list.AddEntry(entry);
00380 }
00381 }
00382
00383 if (list.IsEmpty())
00384 source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
00385 else
00386 {
00387 std::vector<Anope::string> replies;
00388 list.Process(replies);
00389
00390 source.Reply(_("Access list for %s:"), ci->name.c_str());
00391
00392 for (unsigned i = 0; i < replies.size(); ++i)
00393 source.Reply(replies[i]);
00394
00395 source.Reply(_("End of access list"));
00396 }
00397
00398 return;
00399 }
00400
00401 void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00402 {
00403 if (!ci->GetAccessCount())
00404 {
00405 source.Reply(_("%s access list is empty."), ci->name.c_str());
00406 return;
00407 }
00408
00409 ListFormatter list;
00410 list.AddColumn("Number").AddColumn("Level").AddColumn("Mask");
00411 this->ProcessList(source, ci, params, list);
00412 }
00413
00414 void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00415 {
00416 if (!ci->GetAccessCount())
00417 {
00418 source.Reply(_("%s access list is empty."), ci->name.c_str());
00419 return;
00420 }
00421
00422 ListFormatter list;
00423 list.AddColumn("Number").AddColumn("Level").AddColumn("Mask").AddColumn("By").AddColumn("Last seen");
00424 this->ProcessList(source, ci, params, list);
00425 }
00426
00427 void DoClear(CommandSource &source, ChannelInfo *ci)
00428 {
00429 if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
00430 source.Reply(ACCESS_DENIED);
00431 else
00432 {
00433 FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, source));
00434
00435 ci->ClearAccess();
00436
00437 source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str());
00438
00439 bool override = !source.IsFounder(ci);
00440 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
00441 }
00442
00443 return;
00444 }
00445
00446 public:
00447 CommandCSAccess(Module *creator) : Command(creator, "chanserv/access", 2, 4)
00448 {
00449 this->SetDesc(_("Modify the list of privileged users"));
00450 this->SetSyntax(_("\037channel\037 ADD \037mask\037 \037level\037"));
00451 this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
00452 this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
00453 this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037list\037]"));
00454 this->SetSyntax(_("\037channel\037 CLEAR"));
00455 }
00456
00457 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00458 {
00459 const Anope::string &cmd = params[1];
00460 const Anope::string &nick = params.size() > 2 ? params[2] : "";
00461 const Anope::string &s = params.size() > 3 ? params[3] : "";
00462
00463 ChannelInfo *ci = ChannelInfo::Find(params[0]);
00464 if (ci == NULL)
00465 {
00466 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
00467 return;
00468 }
00469
00470 bool is_list = cmd.equals_ci("LIST") || cmd.equals_ci("VIEW");
00471 bool is_clear = cmd.equals_ci("CLEAR");
00472 bool is_del = cmd.equals_ci("DEL");
00473
00474 bool has_access = false;
00475 if (source.HasPriv("chanserv/access/modify"))
00476 has_access = true;
00477 else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
00478 has_access = true;
00479 else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
00480 has_access = true;
00481 else if (is_del)
00482 {
00483 const NickAlias *na = NickAlias::Find(nick);
00484 if (na && na->nc == source.GetAccount())
00485 has_access = true;
00486 }
00487
00488
00489
00490
00491 if (is_list || is_clear ? 0 : (cmd.equals_ci("DEL") ? (nick.empty() || !s.empty()) : s.empty()))
00492 this->OnSyntaxError(source, cmd);
00493 else if (!has_access)
00494 source.Reply(ACCESS_DENIED);
00495 else if (Anope::ReadOnly && !is_list)
00496 source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
00497 else if (cmd.equals_ci("ADD"))
00498 this->DoAdd(source, ci, params);
00499 else if (cmd.equals_ci("DEL"))
00500 this->DoDel(source, ci, params);
00501 else if (cmd.equals_ci("LIST"))
00502 this->DoList(source, ci, params);
00503 else if (cmd.equals_ci("VIEW"))
00504 this->DoView(source, ci, params);
00505 else if (cmd.equals_ci("CLEAR"))
00506 this->DoClear(source, ci);
00507 else
00508 this->OnSyntaxError(source, "");
00509
00510 return;
00511 }
00512
00513 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00514 {
00515 this->SendSyntax(source);
00516 source.Reply(" ");
00517 source.Reply(_("Maintains the \002access list\002 for a channel. The access\n"
00518 "list specifies which users are allowed chanop status or\n"
00519 "access to %s commands on the channel. Different\n"
00520 "user levels allow for access to different subsets of\n"
00521 "privileges. Any registered user not on the access list has\n"
00522 "a user level of 0, and any unregistered user has a user level\n"
00523 "of -1."), source.service->nick.c_str());
00524 source.Reply(" ");
00525 source.Reply(_("The \002ACCESS ADD\002 command adds the given mask to the\n"
00526 "access list with the given user level; if the mask is\n"
00527 "already present on the list, its access level is changed to\n"
00528 "the level specified in the command. The \037level\037 specified\n"
00529 "must be less than that of the user giving the command, and\n"
00530 "if the \037mask\037 is already on the access list, the current\n"
00531 "access level of that nick must be less than the access level\n"
00532 "of the user giving the command. When a user joins the channel\n"
00533 "the access they receive is from the highest level entry in the\n"
00534 "access list."));
00535 source.Reply(" ");
00536 source.Reply(_("The \002ACCESS DEL\002 command removes the given nick from the\n"
00537 "access list. If a list of entry numbers is given, those\n"
00538 "entries are deleted. (See the example for LIST below.)\n"
00539 "You may remove yourself from an access list, even if you\n"
00540 "do not have access to modify that list otherwise."));
00541 source.Reply(" ");
00542 source.Reply(_("The \002ACCESS LIST\002 command displays the access list. If\n"
00543 "a wildcard mask is given, only those entries matching the\n"
00544 "mask are displayed. If a list of entry numbers is given,\n"
00545 "only those entries are shown; for example:\n"
00546 " \002ACCESS #channel LIST 2-5,7-9\002\n"
00547 " Lists access entries numbered 2 through 5 and\n"
00548 " 7 through 9.\n"
00549 " \n"
00550 "The \002ACCESS VIEW\002 command displays the access list similar\n"
00551 "to \002ACCESS LIST\002 but shows the creator and last used time.\n"
00552 " \n"
00553 "The \002ACCESS CLEAR\002 command clears all entries of the\n"
00554 "access list."));
00555 source.Reply(_("\002User access levels\002\n"
00556 " \n"
00557 "By default, the following access levels are defined:\n"
00558 " \n"
00559 " \002Founder\002 Full access to %s functions; automatic\n"
00560 " opping upon entering channel. Note\n"
00561 " that only one person may have founder\n"
00562 " status (it cannot be given using the\n"
00563 " \002ACCESS\002 command).\n"
00564 " \002 10\002 Access to AKICK command; automatic opping.\n"
00565 " \002 5\002 Automatic opping.\n"
00566 " \002 3\002 Automatic voicing.\n"
00567 " \002 0\002 No special privileges; can be opped by other\n"
00568 " ops (unless \002secure-ops\002 is set).\n"
00569 " \n"
00570 "These levels may be changed, or new ones added, using the\n"
00571 "\002LEVELS\002 command; type \002%s%s HELP LEVELS\002 for\n"
00572 "information."), source.service->nick.c_str(), Config->UseStrictPrivMsgString.c_str(), source.service->nick.c_str());
00573 return true;
00574 }
00575 };
00576
00577 class CommandCSLevels : public Command
00578 {
00579 void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00580 {
00581 const Anope::string &what = params[2];
00582 const Anope::string &lev = params[3];
00583
00584 int level;
00585
00586 if (lev.equals_ci("FOUNDER"))
00587 level = ACCESS_FOUNDER;
00588 else
00589 {
00590 try
00591 {
00592 level = convertTo<int>(lev);
00593 }
00594 catch (const ConvertException &)
00595 {
00596 this->OnSyntaxError(source, "SET");
00597 return;
00598 }
00599 }
00600
00601 if (level <= ACCESS_INVALID || level > ACCESS_FOUNDER)
00602 source.Reply(_("Level must be between %d and %d inclusive."), ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
00603 else
00604 {
00605 Privilege *p = PrivilegeManager::FindPrivilege(what);
00606 if (p == NULL)
00607 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->UseStrictPrivMsgString.c_str(), source.service->nick.c_str());
00608 else
00609 {
00610 ci->SetLevel(p->name, level);
00611 FOREACH_MOD(I_OnLevelChange, OnLevelChange(source, ci, p->name, level));
00612
00613 bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
00614 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << p->name << " to level " << level;
00615
00616 if (level == ACCESS_FOUNDER)
00617 source.Reply(_("Level for %s on channel %s changed to founder only."), p->name.c_str(), ci->name.c_str());
00618 else
00619 source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), p->name.c_str(), ci->name.c_str(), level);
00620 }
00621 }
00622 }
00623
00624 void DoDisable(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00625 {
00626 const Anope::string &what = params[2];
00627
00628
00629 if (!what.equals_ci("FOUNDER"))
00630 {
00631 Privilege *p = PrivilegeManager::FindPrivilege(what);
00632 if (p != NULL)
00633 {
00634 ci->SetLevel(p->name, ACCESS_INVALID);
00635 FOREACH_MOD(I_OnLevelChange, OnLevelChange(source, ci, p->name, ACCESS_INVALID));
00636
00637 bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
00638 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable " << p->name;
00639
00640 source.Reply(_("\002%s\002 disabled on channel %s."), p->name.c_str(), ci->name.c_str());
00641 return;
00642 }
00643 }
00644
00645 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->UseStrictPrivMsgString.c_str(), source.service->nick.c_str());
00646
00647 return;
00648 }
00649
00650 void DoList(CommandSource &source, ChannelInfo *ci)
00651 {
00652 source.Reply(_("Access level settings for channel %s:"), ci->name.c_str());
00653
00654 ListFormatter list;
00655 list.AddColumn("Name").AddColumn("Level");
00656
00657 const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
00658
00659 for (unsigned i = 0; i < privs.size(); ++i)
00660 {
00661 const Privilege &p = privs[i];
00662 int16_t j = ci->GetLevel(p.name);
00663
00664 ListFormatter::ListEntry entry;
00665 entry["Name"] = p.name;
00666
00667 if (j == ACCESS_INVALID)
00668 entry["Level"] = "(disabled)";
00669 else if (j == ACCESS_FOUNDER)
00670 entry["Level"] = "(founder only)";
00671 else
00672 entry["Level"] = stringify(j);
00673
00674 list.AddEntry(entry);
00675 }
00676
00677 std::vector<Anope::string> replies;
00678 list.Process(replies);
00679
00680 for (unsigned i = 0; i < replies.size(); ++i)
00681 source.Reply(replies[i]);
00682 }
00683
00684 void DoReset(CommandSource &source, ChannelInfo *ci)
00685 {
00686 reset_levels(ci);
00687 FOREACH_MOD(I_OnLevelChange, OnLevelChange(source, ci, "ALL", 0));
00688
00689 bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
00690 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to reset all levels";
00691
00692 source.Reply(_("Access levels for \002%s\002 reset to defaults."), ci->name.c_str());
00693 return;
00694 }
00695
00696 public:
00697 CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4)
00698 {
00699 this->SetDesc(_("Redefine the meanings of access levels"));
00700 this->SetSyntax(_("\037channel\037 SET \037type\037 \037level\037"));
00701 this->SetSyntax(_("\037channel\037 {DIS | DISABLE} \037type\037"));
00702 this->SetSyntax(_("\037channel\037 LIST"));
00703 this->SetSyntax(_("\037channel\037 RESET"));
00704 }
00705
00706 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00707 {
00708 const Anope::string &cmd = params[1];
00709 const Anope::string &what = params.size() > 2 ? params[2] : "";
00710 const Anope::string &s = params.size() > 3 ? params[3] : "";
00711
00712 ChannelInfo *ci = ChannelInfo::Find(params[0]);
00713 if (ci == NULL)
00714 {
00715 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
00716 return;
00717 }
00718
00719
00720
00721
00722 if (cmd.equals_ci("SET") ? s.empty() : (cmd.substr(0, 3).equals_ci("DIS") ? (what.empty() || !s.empty()) : !what.empty()))
00723 this->OnSyntaxError(source, cmd);
00724 else if (!source.AccessFor(ci).HasPriv("FOUNDER") && !source.HasPriv("chanserv/access/modify"))
00725 source.Reply(ACCESS_DENIED);
00726 else if (cmd.equals_ci("SET"))
00727 this->DoSet(source, ci, params);
00728 else if (cmd.equals_ci("DIS") || cmd.equals_ci("DISABLE"))
00729 this->DoDisable(source, ci, params);
00730 else if (cmd.equals_ci("LIST"))
00731 this->DoList(source, ci);
00732 else if (cmd.equals_ci("RESET"))
00733 this->DoReset(source, ci);
00734 else
00735 this->OnSyntaxError(source, "");
00736
00737 return;
00738 }
00739
00740 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00741 {
00742 if (subcommand.equals_ci("DESC"))
00743 {
00744 source.Reply(_("The following feature/function names are available:"));
00745
00746 ListFormatter list;
00747 list.AddColumn("Name").AddColumn("Description");
00748
00749 const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
00750 for (unsigned i = 0; i < privs.size(); ++i)
00751 {
00752 const Privilege &p = privs[i];
00753 ListFormatter::ListEntry entry;
00754 entry["Name"] = p.name;
00755 entry["Description"] = Language::Translate(source.nc, p.desc.c_str());
00756 list.AddEntry(entry);
00757 }
00758
00759 std::vector<Anope::string> replies;
00760 list.Process(replies);
00761
00762 for (unsigned i = 0; i < replies.size(); ++i)
00763 source.Reply(replies[i]);
00764 }
00765 else
00766 {
00767 this->SendSyntax(source);
00768 source.Reply(" ");
00769 source.Reply(_("The \002LEVELS\002 command allows fine control over the meaning of\n"
00770 "the numeric access levels used for channels. With this\n"
00771 "command, you can define the access level required for most\n"
00772 "of %s's functions. (The \002SET FOUNDER\002 and this command\n"
00773 "are always restricted to the channel founder.)\n"
00774 " \n"
00775 "\002LEVELS SET\002 allows the access level for a function or group of\n"
00776 "functions to be changed. \002LEVELS DISABLE\002 (or \002DIS\002 for short)\n"
00777 "disables an automatic feature or disallows access to a\n"
00778 "function by anyone, INCLUDING the founder (although, the founder\n"
00779 "can always reenable it).\n"
00780 " \n"
00781 "\002LEVELS LIST\002 shows the current levels for each function or\n"
00782 "group of functions. \002LEVELS RESET\002 resets the levels to the\n"
00783 "default levels of a newly-created channel (see\n"
00784 "\002HELP ACCESS LEVELS\002).\n"
00785 " \n"
00786 "For a list of the features and functions whose levels can be\n"
00787 "set, see \002HELP LEVELS DESC\002."), source.service->nick.c_str());
00788 }
00789 return true;
00790 }
00791 };
00792
00793 class CSAccess : public Module
00794 {
00795 AccessAccessProvider accessprovider;
00796 CommandCSAccess commandcsaccess;
00797 CommandCSLevels commandcslevels;
00798
00799 public:
00800 CSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00801 accessprovider(this), commandcsaccess(this), commandcslevels(this)
00802 {
00803 this->SetAuthor("Anope");
00804 this->SetPermanent(true);
00805
00806 Implementation i[] = { I_OnReload, I_OnCreateChan, I_OnGroupCheckPriv };
00807 ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation));
00808
00809 try
00810 {
00811 this->OnReload();
00812 }
00813 catch (const ConfigException &ex)
00814 {
00815 throw ModuleException(ex.GetReason());
00816 }
00817 }
00818
00819 void OnReload() anope_override
00820 {
00821 defaultLevels.clear();
00822 ConfigReader config;
00823
00824 for (int i = 0; i < config.Enumerate("privilege"); ++i)
00825 {
00826 const Anope::string &pname = config.ReadValue("privilege", "name", "", i);
00827
00828 Privilege *p = PrivilegeManager::FindPrivilege(pname);
00829 if (p == NULL)
00830 continue;
00831
00832 const Anope::string &value = config.ReadValue("privilege", "level", "", i);
00833 if (value.empty())
00834 continue;
00835 else if (value.equals_ci("founder"))
00836 defaultLevels[p->name] = ACCESS_FOUNDER;
00837 else if (value.equals_ci("disabled"))
00838 defaultLevels[p->name] = ACCESS_INVALID;
00839 else
00840 defaultLevels[p->name] = config.ReadInteger("privilege", "level", i, false);
00841 }
00842 }
00843
00844 void OnCreateChan(ChannelInfo *ci) anope_override
00845 {
00846 reset_levels(ci);
00847 }
00848
00849 EventReturn OnGroupCheckPriv(const AccessGroup *group, const Anope::string &priv) anope_override
00850 {
00851 if (group->ci == NULL)
00852 return EVENT_CONTINUE;
00853
00854 int16_t level = group->ci->GetLevel(priv);
00855 if (level < 0)
00856 return EVENT_ALLOW;
00857 else if (level == 0 && group->nc)
00858 return EVENT_ALLOW;
00859 return EVENT_CONTINUE;
00860 }
00861 };
00862
00863 MODULE_INIT(CSAccess)