diff options
Diffstat (limited to 'modules')
58 files changed, 1331 insertions, 800 deletions
diff --git a/modules/core/bs_assign.cpp b/modules/core/bs_assign.cpp index 0a08eec3b..803368994 100644 --- a/modules/core/bs_assign.cpp +++ b/modules/core/bs_assign.cpp @@ -49,7 +49,7 @@ class CommandBSAssign : public Command return; } - if (ci->botflags.HasFlag(BS_NOBOT) || (!check_access(u, ci, CA_ASSIGN) && !u->HasPriv("botserv/administration"))) + if (ci->botflags.HasFlag(BS_NOBOT) || (!ci->HasPriv(u, CA_ASSIGN) && !u->HasPriv("botserv/administration"))) { source.Reply(ACCESS_DENIED); return; @@ -67,7 +67,7 @@ class CommandBSAssign : public Command return; } - bool override = !check_access(u, ci, CA_ASSIGN); + bool override = !ci->HasPriv(u, CA_ASSIGN); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << bi->nick; bi->Assign(u, ci); @@ -111,7 +111,7 @@ class CommandBSUnassign : public Command return; } - if (!u->HasPriv("botserv/administration") && !check_access(u, ci, CA_ASSIGN)) + if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_ASSIGN)) { source.Reply(ACCESS_DENIED); return; @@ -129,7 +129,7 @@ class CommandBSUnassign : public Command return; } - bool override = !check_access(u, ci, CA_ASSIGN); + bool override = !ci->HasPriv(u, CA_ASSIGN); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "for " << ci->bi->nick; ci->bi->UnAssign(u, ci); diff --git a/modules/core/bs_badwords.cpp b/modules/core/bs_badwords.cpp index a272bdff9..a27dca2d4 100644 --- a/modules/core/bs_badwords.cpp +++ b/modules/core/bs_badwords.cpp @@ -60,7 +60,7 @@ class BadwordsDelCallback : public NumberList public: BadwordsDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), Deleted(0), override(false) { - if (!check_access(source.u, ci, CA_BADWORDS) && source.u->HasPriv("botserv/administration")) + if (!ci->HasPriv(source.u, CA_BADWORDS) && source.u->HasPriv("botserv/administration")) this->override = true; } @@ -90,7 +90,7 @@ class CommandBSBadwords : public Command private: void DoList(CommandSource &source, ChannelInfo *ci, const Anope::string &word) { - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "LIST"; if (!ci->GetBadWordCount()) @@ -167,7 +167,7 @@ class CommandBSBadwords : public Command } } - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "ADD " << realword; ci->AddBadWord(realword, type); @@ -203,7 +203,7 @@ class CommandBSBadwords : public Command return; } - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "DEL " << badword->word; source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str()); @@ -216,7 +216,7 @@ class CommandBSBadwords : public Command void DoClear(CommandSource &source, ChannelInfo *ci) { - bool override = !check_access(source.u, ci, CA_BADWORDS); + bool override = !ci->HasPriv(source.u, CA_BADWORDS); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source.u, this, ci) << "CLEAR"; ci->ClearBadWords(); @@ -254,7 +254,7 @@ class CommandBSBadwords : public Command } - if (!check_access(u, ci, CA_BADWORDS) && (!need_args || !u->HasPriv("botserv/administration"))) + if (!ci->HasPriv(u, CA_BADWORDS) && (!need_args || !u->HasPriv("botserv/administration"))) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/bs_control.cpp b/modules/core/bs_control.cpp index 431739693..16a5b372a 100644 --- a/modules/core/bs_control.cpp +++ b/modules/core/bs_control.cpp @@ -35,7 +35,7 @@ class CommandBSSay : public Command return; } - if (!check_access(u, ci, CA_SAY)) + if (!ci->HasPriv(u, CA_SAY)) { source.Reply(ACCESS_DENIED); return; @@ -98,7 +98,7 @@ class CommandBSAct : public Command return; } - if (!check_access(u, ci, CA_SAY)) + if (!ci->HasPriv(u, CA_SAY)) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/bs_info.cpp b/modules/core/bs_info.cpp index 38178a07b..b48a0b296 100644 --- a/modules/core/bs_info.cpp +++ b/modules/core/bs_info.cpp @@ -70,7 +70,7 @@ class CommandBSInfo : public Command } else if ((ci = cs_findchan(query))) { - if (!check_access(u, ci, CA_FOUNDER) && !u->HasPriv("botserv/administration")) + if (!ci->HasPriv(u, CA_FOUNDER) && !u->HasPriv("botserv/administration")) { source.Reply(ACCESS_DENIED); return; diff --git a/modules/core/bs_kick.cpp b/modules/core/bs_kick.cpp index a8c34e5cd..3f433f773 100644 --- a/modules/core/bs_kick.cpp +++ b/modules/core/bs_kick.cpp @@ -41,13 +41,13 @@ class CommandBSKick : public Command this->OnSyntaxError(source, ""); else if (!value.equals_ci("ON") && !value.equals_ci("OFF")) this->OnSyntaxError(source, ""); - else if (!check_access(u, ci, CA_SET) && !u->HasPriv("botserv/administration")) + else if (!ci->HasPriv(u, CA_SET) && !u->HasPriv("botserv/administration")) source.Reply(ACCESS_DENIED); else if (!ci->bi) source.Reply(BOT_NOT_ASSIGNED); else { - bool override = !check_access(u, ci, CA_SET); + bool override = !ci->HasPriv(u, CA_SET); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << option << " " << value; if (option.equals_ci("BADWORDS")) @@ -768,7 +768,7 @@ class BSKick : public Module return; bool Allow = true; - if (check_access(u, ci, CA_NOKICK)) + if (ci->HasPriv(u, CA_NOKICK)) Allow = false; else if (ci->botflags.HasFlag(BS_DONTKICKOPS) && (c->HasUserStatus(u, CMODE_HALFOP) || c->HasUserStatus(u, CMODE_OP) || c->HasUserStatus(u, CMODE_PROTECT) || c->HasUserStatus(u, CMODE_OWNER))) Allow = false; @@ -993,7 +993,7 @@ class BSKick : public Module Channel *chan = (*it)->chan; ++it; - if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !check_access(u, chan->ci, CA_NOKICK)) + if (chan->ci != NULL && chan->ci->botflags.HasFlag(BS_KICK_AMSGS) && !chan->ci->HasPriv(u, CA_NOKICK)) { check_ban(chan->ci, u, TTB_AMSGS); bot_kick(chan->ci, u, _("Don't use AMSGs!")); diff --git a/modules/core/bs_main.cpp b/modules/core/bs_main.cpp index 515b99ae0..69eab6040 100644 --- a/modules/core/bs_main.cpp +++ b/modules/core/bs_main.cpp @@ -81,7 +81,7 @@ class BotServCore : public Module if (bi == NULL || !bi->commands.count(command)) return; - if (check_access(u, c->ci, CA_FANTASIA)) + if (c->ci->HasPriv(u, CA_FANTASIA)) { this->fantasy_channel = c; @@ -125,7 +125,7 @@ class BotServCore : public Module * to has synced, or we'll get greet-floods when the net * recovers from a netsplit. -GD */ - if (c->FindUser(c->ci->bi) && c->ci->botflags.HasFlag(BS_GREET) && user->Account() && !user->Account()->greet.empty() && check_access(user, c->ci, CA_GREET) && user->server->IsSynced()) + if (c->FindUser(c->ci->bi) && c->ci->botflags.HasFlag(BS_GREET) && user->Account() && !user->Account()->greet.empty() && c->ci->HasPriv(user, CA_GREET) && user->server->IsSynced()) { ircdproto->SendPrivmsg(c->ci->bi, c->name, "[%s] %s", user->Account()->display.c_str(), user->Account()->greet.c_str()); c->ci->bi->lastmsg = Anope::CurTime; diff --git a/modules/core/bs_set.cpp b/modules/core/bs_set.cpp index 0fbba874e..ccd1bb615 100644 --- a/modules/core/bs_set.cpp +++ b/modules/core/bs_set.cpp @@ -59,11 +59,11 @@ class CommandBSSet : public Command } else if (!(ci = cs_findchan(chan))) source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); - else if (!u->HasPriv("botserv/administration") && !check_access(u, ci, CA_SET)) + else if (!u->HasPriv("botserv/administration") && !ci->HasPriv(u, CA_SET)) source.Reply(ACCESS_DENIED); else { - bool override = !check_access(u, ci, CA_SET); + bool override = !ci->HasPriv(u, CA_SET); Log(override ? LOG_ADMIN : LOG_COMMAND, u, this, ci) << option << " " << value; if (option.equals_ci("DONTKICKOPS")) diff --git a/modules/core/cs_access.cpp b/modules/core/cs_access.cpp index 643292c38..777b0c176 100644 --- a/modules/core/cs_access.cpp +++ b/modules/core/cs_access.cpp @@ -13,6 +13,132 @@ #include "module.h" +enum +{ + ACCESS_INVALID = -10000, + ACCESS_FOUNDER = 10001 +}; + +static struct AccessLevels +{ + ChannelAccess priv; + int default_level; + Anope::string config_name; + Anope::string name; + Anope::string desc; +} defaultLevels[] = { + { CA_ACCESS_CHANGE, 10, "level_change", "ACC-CHANGE", _("Allowed to modify the access list") }, + { CA_ACCESS_LIST, 1, "level_list", "ACC-LIST", _("Allowed to view the access list") }, + { CA_AKICK, 10, "level_akick", "AKICK", _("Allowed to use AKICK command") }, + { CA_ASSIGN, ACCESS_FOUNDER, "level_assign", "ASSIGN", _("Allowed to assign/unassign a bot") }, + { CA_AUTOHALFOP, 4, "level_autohalfop", "AUTOHALFOP", _("Automatic mode +h") }, + { CA_AUTOOP, 5, "level_autoop", "AUTOOP", _("Automatic channel operator status") }, + { CA_AUTOOWNER, 10000, "level_autoowner", "AUTOOWNER", _("Automatic mode +q") }, + { CA_AUTOPROTECT, 10, "level_autoprotect", "AUTOPROTECT", _("Automatic mode +a") }, + { CA_AUTOVOICE, 3, "level_autovoice", "AUTOVOICE", _("Automatic mode +v") }, + { CA_BADWORDS, 10, "level_badwords", "BADWORDS", _("Allowed to modify channel badwords list") }, + { CA_BAN, 4, "level_ban", "BAN", _("Allowed to use ban users") }, + { CA_FANTASIA, 3, "level_fantasia", "FANTASIA", _("Allowed to use fantaisist commands") }, + { CA_FOUNDER, ACCESS_FOUNDER, "level_founder", "FOUNDER", _("Allowed to issue commands restricted to channel founders") }, + { CA_GETKEY, 5, "level_getkey", "GETKEY", _("Allowed to use GETKEY command") }, + { CA_GREET, 5, "level_greet", "GREET", _("Greet message displayed") }, + { CA_HALFOP, 5, "level_halfop", "HALFOP", _("Allowed to (de)halfop users") }, + { CA_HALFOPME, 4, "level_halfopme", "HALFOPME", _("Allowed to (de)halfop him/herself") }, + { CA_INFO, 10000, "level_info", "INFO", _("Allowed to use INFO command with ALL option") }, + { CA_INVITE, 5, "level_invite", "INVITE", _("Allowed to use the INVITE command") }, + { CA_KICK, 4, "level_kick", "KICK", _("Allowed to use the KICK command") }, + { CA_MEMO, 10, "level_memo", "MEMO", _("Allowed to read channel memos") }, + { CA_MODE, 5, "level_mode", "MODE", _("Allowed to change channel modes") }, + { CA_NOKICK, 1, "level_nokick", "NOKICK", _("Never kicked by the bot's kickers") }, + { CA_OPDEOP, 5, "level_opdeop", "OPDEOP", _("Allowed to (de)op users") }, + { CA_OPDEOPME, 5, "level_opdeopme", "OPDEOPME", _("Allowed to (de)op him/herself") }, + { CA_OWNER, ACCESS_FOUNDER, "level_owner", "OWNER", _("Allowed to use (de)owner users") }, + { CA_OWNERME, 10000, "level_ownerme", "OWNERME", _("Allowed to (de)owner him/herself") }, + { CA_PROTECT, 10000, "level_protect", "PROTECT", _("Allowed to (de)protect users") }, + { CA_PROTECTME, 10, "level_protectme", "PROTECTME", _("Allowed to (de)protect him/herself"), }, + { CA_SAY, 5, "level_say", "SAY", _("Allowed to use SAY and ACT commands") }, + { CA_SIGNKICK, ACCESS_FOUNDER, "level_signkick", "SIGNKICK", _("No signed kick when SIGNKICK LEVEL is used") }, + { CA_SET, 10000, "level_set", "SET", _("Allowed to set channel settings") }, + { CA_TOPIC, 5, "level_topic", "TOPIC", _("Allowed to change channel topics") }, + { CA_UNBAN, 4, "level_unban", "UNBAN", _("Allowed to unban users") }, + { CA_VOICE, 4, "level_voice", "VOICE", _("Allowed to (de)voice users") }, + { CA_VOICEME, 3, "level_voiceme", "VOICEME", _("Allowed to (de)voice him/herself") }, + { CA_SIZE, -1, "", "", "" } +}; + +static void reset_levels(ChannelInfo *ci) +{ + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + ci->levels[defaultLevels[i].priv] = defaultLevels[i].default_level; +} + +class AccessChanAccess : public ChanAccess +{ + public: + int level; + + AccessChanAccess(AccessProvider *p) : ChanAccess(p) + { + } + + bool Matches(User *u, NickCore *nc) + { + if (u && (Anope::Match(u->nick, this->mask) || Anope::Match(u->GetMask(), this->mask))) + return true; + else if (nc && Anope::Match(nc->display, this->mask)) + return true; + return false; + } + + bool HasPriv(ChannelAccess priv) + { + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + if (defaultLevels[i].priv == priv) + return DetermineLevel(this) >= this->ci->levels[priv]; + return false; + } + + Anope::string Serialize() + { + return stringify(this->level); + } + + void Unserialize(const Anope::string &data) + { + this->level = convertTo<int>(data); + } + + static int DetermineLevel(ChanAccess *access) + { + if (access->provider->name == "access/access") + { + AccessChanAccess *aaccess = debug_cast<AccessChanAccess *>(access); + return aaccess->level; + } + else + { + int highest = 1; + for (int i = 0; defaultLevels[i].priv != CA_SIZE; ++i) + if (access->ci->levels[defaultLevels[i].priv] > highest && access->HasPriv(defaultLevels[i].priv)) + highest = access->ci->levels[defaultLevels[i].priv]; + return highest; + } + } +}; + +class AccessAccessProvider : public AccessProvider +{ + public: + AccessAccessProvider(Module *o) : AccessProvider(o, "access/access") + { + } + + ChanAccess *Create() + { + return new AccessChanAccess(this); + } +}; + class AccessListCallback : public NumberList { protected: @@ -27,7 +153,7 @@ class AccessListCallback : public NumberList ~AccessListCallback() { if (SentHeader) - source.Reply(_("End of access list."), ci->name.c_str()); + source.Reply(_("End of access list.")); else source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); } @@ -48,13 +174,7 @@ class AccessListCallback : public NumberList static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) { - if (ci->HasFlag(CI_XOP)) - { - Anope::string xop = get_xop_level(access->level); - source.Reply(_(" %3d %s %s"), Number + 1, xop.c_str(), access->GetMask().c_str()); - } - else - source.Reply(_(" %3d %4d %s"), Number + 1, access->level, access->GetMask().c_str()); + source.Reply(_(" %3d %4d %s"), Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str()); } }; @@ -82,20 +202,19 @@ class AccessViewCallback : public AccessListCallback static void DoList(CommandSource &source, ChannelInfo *ci, unsigned Number, ChanAccess *access) { Anope::string timebuf; - if (ci->c && nc_on_chan(ci->c, access->nc)) - timebuf = "Now"; - else if (access->last_seen == 0) - timebuf = "Never"; - else - timebuf = do_strftime(access->last_seen); - - if (ci->HasFlag(CI_XOP)) + if (ci->c) + for (CUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) + if (access->Matches((*cit)->user, (*cit)->user->Account())) + timebuf = "Now"; + if (timebuf.empty()) { - Anope::string xop = get_xop_level(access->level); - source.Reply(CHAN_ACCESS_VIEW_XOP_FORMAT, Number + 1, xop.c_str(), access->GetMask().c_str(), access->creator.c_str(), timebuf.c_str()); + if (access->last_seen == 0) + timebuf = "Never"; + else + timebuf = do_strftime(access->last_seen); } - else - source.Reply(CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, access->level, access->GetMask().c_str(), access->creator.c_str(), timebuf.c_str()); + + source.Reply(CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, AccessChanAccess::DetermineLevel(access), access->mask.c_str(), access->creator.c_str(), timebuf.c_str()); } }; @@ -111,7 +230,7 @@ class AccessDelCallback : public NumberList public: AccessDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), Deleted(0), Denied(false) { - if (!check_access(source.u, ci, CA_ACCESS_CHANGE) && source.u->HasPriv("chanserv/access/modify")) + if (!ci->HasPriv(source.u, CA_ACCESS_CHANGE) && source.u->HasPriv("chanserv/access/modify")) this->override = true; } @@ -141,9 +260,10 @@ class AccessDelCallback : public NumberList ChanAccess *access = ci->GetAccess(Number - 1); - ChanAccess *u_access = ci->GetAccess(u); - int16 u_level = u_access ? u_access->level : 0; - if (u_level <= access->level && !u->HasPriv("chanserv/access/modify")) + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *u_highest = u_access.Highest(); + + if (u_highest ? AccessChanAccess::DetermineLevel(u_highest) : 0 <= AccessChanAccess::DetermineLevel(access) && !u->HasPriv("chanserv/access/modify")) { Denied = true; return; @@ -151,9 +271,9 @@ class AccessDelCallback : public NumberList ++Deleted; if (!Nicks.empty()) - Nicks += ", " + access->GetMask(); + Nicks += ", " + access->mask; else - Nicks = access->GetMask(); + Nicks = access->mask; FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access)); @@ -176,17 +296,18 @@ class CommandCSAccess : public Command } catch (const ConvertException &) { } - ChanAccess *u_access = ci->GetAccess(u); - int16 u_level = u_access ? u_access->level : 0; - if (level >= u_level && !u->HasPriv("chanserv/access/modify")) + if (!level) { - source.Reply(ACCESS_DENIED); + source.Reply(_("Access level must be non-zero.")); return; } - if (!level) + AccessGroup u_access = ci->AccessFor(u); + ChanAccess *highest = u_access.Highest(); + int u_level = (highest ? AccessChanAccess::DetermineLevel(highest) : 0); + if (!ci->HasPriv(u, CA_FOUNDER) && level >= u_level && !u->HasPriv("chanserv/access/modify")) { - source.Reply(_("Access level must be non-zero.")); + source.Reply(ACCESS_DENIED); return; } else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) @@ -195,29 +316,22 @@ class CommandCSAccess : public Command return; } - bool override = !check_access(u, ci, CA_ACCESS_CHANGE) || level >= u_level; + bool override = !ci->HasPriv(u, CA_ACCESS_CHANGE) || level >= u_level; - ChanAccess *access = ci->GetAccess(mask, 0, false); - if (access) + for (unsigned i = ci->GetAccessCount(); i > 0; --i) { - /* Don't allow lowering from a level >= u_level */ - if (access->level >= u_level && !u->HasPriv("chanserv/access/modify")) - { - source.Reply(ACCESS_DENIED); - return; - } - if (access->level == level) + ChanAccess *access = ci->GetAccess(i - 1); + if (mask.equals_ci(access->mask)) { - source.Reply(_("Access level for \002%s\002 on %s unchanged from \002%d\002."), access->GetMask().c_str(), ci->name.c_str(), level); - return; + /* Don't allow lowering from a level >= u_level */ + if (AccessChanAccess::DetermineLevel(access) >= u_level && !u->HasPriv("chanserv/access/modify")) + { + source.Reply(ACCESS_DENIED); + return; + } + ci->EraseAccess(i - 1); + break; } - access->level = level; - - FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, access)); - - Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " (level: " << level << ") as level " << u_level; - source.Reply(_("Access level for \002%s\002 on %s changed to \002%d\002."), access->GetMask().c_str(), ci->name.c_str(), level); - return; } if (ci->GetAccessCount() >= Config->CSAccessMax) @@ -226,12 +340,25 @@ class CommandCSAccess : public Command return; } - access = ci->AddAccess(mask, level, u->nick); + if (mask.find_first_of("!*@") == Anope::string::npos && findnick(mask) == NULL) + mask += "!*@*"; + + service_reference<AccessProvider> provider("access/access"); + if (!provider) + return; + AccessChanAccess *access = debug_cast<AccessChanAccess *>(provider->Create()); + access->ci = ci; + access->mask = mask; + access->creator = u->nick; + access->level = level; + access->last_seen = 0; + access->created = Anope::CurTime; + ci->AddAccess(access); FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, access)); Log(override ? LOG_OVERRIDE : LOG_COMMAND, u, this, ci) << "ADD " << mask << " (level: " << level << ") as level " << u_level; - source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->GetMask().c_str(), ci->name.c_str(), level); + source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->mask.c_str(), ci->name.c_str(), level); return; } @@ -251,23 +378,32 @@ class CommandCSAccess : public Command } else { - ChanAccess *access = ci->GetAccess(mask, 0, false); - ChanAccess *u_access = ci->GetAccess(u); - int16 u_level = u_access ? u_access->level : 0; - if (!access) - source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str()); - else if (access->nc != u->Account() && check_access(u, ci, CA_NOJOIN) && u_level <= access->level && !u->HasPriv("chanserv/access/modify")) - source.Reply(ACCESS_DENIED); - else - { - source.Reply(_("\002%s\002 deleted from %s access list."), access->GetMask().c_str(), ci->name.c_str()); |
