From ee44af8d04f23b4a16c9b377764f930d69e575f7 Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Sun, 27 Nov 2022 07:32:42 +0000 Subject: Refactor the internals of the oper system. - Allow overriding privileges from the blocks in the and blocks. - Separate oper types from oper accounts in the code. This enables moving some core stuff out of the config tag later. - Merge the config tags together to make a synthetic tag that can have getXXX called on it instead of using getConfig and then converting it. - Move the details of Have*Permission into the oper type class. - Improve oper events to allow modules to easily hook into the oper system. --- src/channels.cpp | 7 +- src/command_parse.cpp | 2 +- src/configparser.cpp | 15 -- src/configreader.cpp | 66 ++++---- src/coremods/core_info/cmd_servlist.cpp | 4 +- src/coremods/core_oper/cmd_oper.cpp | 10 +- src/coremods/core_oper/core_oper.cpp | 15 +- src/coremods/core_oper/core_oper.h | 7 + src/coremods/core_oper/umode_o.cpp | 2 +- src/coremods/core_oper/umode_s.cpp | 2 +- src/coremods/core_stats.cpp | 13 +- src/coremods/core_whois.cpp | 2 +- src/mode.cpp | 2 +- src/modules.cpp | 8 +- src/modules/m_check.cpp | 21 ++- src/modules/m_httpd_stats.cpp | 2 +- src/modules/m_ldapoper.cpp | 16 +- src/modules/m_operchans.cpp | 2 +- src/modules/m_operjoin.cpp | 4 +- src/modules/m_operlevels.cpp | 4 +- src/modules/m_opermodes.cpp | 4 +- src/modules/m_opermotd.cpp | 2 +- src/modules/m_operprefix.cpp | 4 +- src/modules/m_override.cpp | 4 +- src/modules/m_spanningtree/commands.h | 2 +- src/modules/m_spanningtree/main.cpp | 6 +- src/modules/m_spanningtree/main.h | 2 +- src/modules/m_spanningtree/netburst.cpp | 2 +- src/modules/m_spanningtree/opertype.cpp | 24 +-- src/modules/m_sqloper.cpp | 21 +-- src/modules/m_sslinfo.cpp | 45 ++---- src/modules/m_swhois.cpp | 7 +- src/usermanager.cpp | 2 +- src/users.cpp | 266 +++++++++++++++++++------------- 34 files changed, 304 insertions(+), 291 deletions(-) (limited to 'src') diff --git a/src/channels.cpp b/src/channels.cpp index 591f3368b..d98bcc123 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -188,11 +188,8 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co { unsigned long maxchans = user->GetClass()->maxchans; if (user->IsOper()) - { - unsigned long opermaxchans = ConvToNum(user->oper->getConfig("maxchans")); - if (opermaxchans > maxchans) - maxchans = opermaxchans; - } + maxchans = user->oper->GetConfig()->getUInt("maxchans", maxchans, maxchans); + if (user->chans.size() >= maxchans) { user->WriteNumeric(ERR_TOOMANYCHANNELS, cname, "You are on too many channels"); diff --git a/src/command_parse.cpp b/src/command_parse.cpp index 62b7dcade..950a71c7e 100644 --- a/src/command_parse.cpp +++ b/src/command_parse.cpp @@ -281,7 +281,7 @@ void CommandParser::ProcessCommand(LocalUser* user, std::string& command, Comman { user->CommandFloodPenalty += failpenalty; user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to command %s", - user->oper->name.c_str(), command.c_str())); + user->oper->GetType().c_str(), command.c_str())); FOREACH_MOD(OnCommandBlocked, (command, command_p, user)); return; } diff --git a/src/configparser.cpp b/src/configparser.cpp index 5e892fdfe..04ddd763b 100644 --- a/src/configparser.cpp +++ b/src/configparser.cpp @@ -750,18 +750,3 @@ ConfigTag::ConfigTag(const std::string& Name, const FilePosition& Source) , source(Source) { } - -OperInfo::OperInfo(const std::string& Name) - : name(Name) -{ -} - -std::string OperInfo::getConfig(const std::string& key) -{ - std::string rv; - if (type_block) - type_block->readString(key, rv); - if (oper_block) - oper_block->readString(key, rv); - return rv; -} diff --git a/src/configreader.cpp b/src/configreader.cpp index d91f5a389..486bf3ad8 100644 --- a/src/configreader.cpp +++ b/src/configreader.cpp @@ -91,61 +91,63 @@ ServerConfig::ServerConfig() { } -typedef std::map> LocalIndex; -void ServerConfig::CrossCheckOperClassType() +void ServerConfig::CrossCheckOperBlocks() { - LocalIndex operclass; + std::unordered_map> operclass; for (const auto& [_, tag] : ConfTags("class")) { - std::string name = tag->getString("name"); + const std::string name = tag->getString("name"); if (name.empty()) throw CoreException(" missing from tag at " + tag->source.str()); - if (operclass.find(name) != operclass.end()) + + if (!operclass.emplace(name, tag).second) throw CoreException("Duplicate class block with name " + name + " at " + tag->source.str()); - operclass[name] = tag; } for (const auto& [_, tag] : ConfTags("type")) { - std::string name = tag->getString("name"); + const std::string name = tag->getString("name"); if (name.empty()) throw CoreException(" is missing from tag at " + tag->source.str()); - if (OperTypes.find(name) != OperTypes.end()) - throw CoreException("Duplicate type block with name " + name + " at " + tag->source.str()); - auto ifo = std::make_shared(name); - OperTypes[name] = ifo; - ifo->type_block = tag; + auto type = std::make_shared(name, nullptr); - std::string classname; - irc::spacesepstream str(tag->getString("classes")); - while (str.GetToken(classname)) + // Copy the settings from the oper class. + irc::spacesepstream classlist(tag->getString("classes")); + for (std::string classname; classlist.GetToken(classname); ) { - LocalIndex::iterator cls = operclass.find(classname); - if (cls == operclass.end()) - throw CoreException("Oper type " + name + " has missing class " + classname); - ifo->class_blocks.push_back(cls->second); + auto klass = operclass.find(classname); + if (klass == operclass.end()) + throw CoreException("Oper type " + name + " has missing class " + classname + " at " + tag->source.str()); + + // Apply the settings from the class. + type->Configure(klass->second, false); } + + // Once the classes have been applied we can apply this. + type->Configure(tag, true); + + if (!OperTypes.emplace(name, type).second) + throw CoreException("Duplicate type block with name " + name + " at " + tag->source.str()); } for (const auto& [_, tag] : ConfTags("oper")) { - std::string name = tag->getString("name"); + const std::string name = tag->getString("name"); if (name.empty()) throw CoreException(" missing from tag at " + tag->source.str()); - std::string type = tag->getString("type"); - OperIndex::iterator tblk = OperTypes.find(type); - if (tblk == OperTypes.end()) - throw CoreException("Oper block " + name + " has missing type " + type); - if (oper_blocks.find(name) != oper_blocks.end()) - throw CoreException("Duplicate oper block with name " + name + " at " + tag->source.str()); + const std::string typestr = tag->getString("type"); + if (typestr.empty()) + throw CoreException(" missing from tag at " + tag->source.str()); - auto ifo = std::make_shared(type); - ifo->oper_block = tag; - ifo->type_block = tblk->second->type_block; - ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end()); - oper_blocks[name] = ifo; + auto type = OperTypes.find(typestr); + if (type == OperTypes.end()) + throw CoreException("Oper block " + name + " has missing type " + typestr + " at " + tag->source.str()); + + auto account = std::make_shared(name, type->second, tag); + if (!OperAccounts.emplace(name, account).second) + throw CoreException("Duplicate oper block with name " + name + " at " + tag->source.str()); } } @@ -408,7 +410,7 @@ void ServerConfig::Apply(ServerConfig* old, const std::string& useruid) Fill(); // Handle special items - CrossCheckOperClassType(); + CrossCheckOperBlocks(); CrossCheckConnectBlocks(old); } catch (const CoreException& ce) diff --git a/src/coremods/core_info/cmd_servlist.cpp b/src/coremods/core_info/cmd_servlist.cpp index 9cb57ed53..40bf6cfdc 100644 --- a/src/coremods/core_info/cmd_servlist.cpp +++ b/src/coremods/core_info/cmd_servlist.cpp @@ -44,7 +44,7 @@ CmdResult CommandServList::HandleLocal(LocalUser* user, const Params& parameters if (serviceuser->IsModeSet(invisiblemode) || !InspIRCd::Match(serviceuser->nick, mask)) continue; - if (has_type && (!serviceuser->IsOper() || !InspIRCd::Match(serviceuser->oper->name, parameters[2]))) + if (has_type && (!serviceuser->IsOper() || !InspIRCd::Match(serviceuser->oper->GetType(), parameters[2]))) continue; Numeric::Numeric numeric(RPL_SERVLIST); @@ -52,7 +52,7 @@ CmdResult CommandServList::HandleLocal(LocalUser* user, const Params& parameters .push(serviceuser->nick) .push(serviceuser->server->GetPublicName()) .push("*") - .push(serviceuser->IsOper() ? serviceuser->oper->name : "*") + .push(serviceuser->IsOper() ? serviceuser->oper->GetType() : "*") .push(0) .push(serviceuser->GetRealName()); user->WriteNumeric(numeric); diff --git a/src/coremods/core_oper/cmd_oper.cpp b/src/coremods/core_oper/cmd_oper.cpp index 786e047e1..344da7c29 100644 --- a/src/coremods/core_oper/cmd_oper.cpp +++ b/src/coremods/core_oper/cmd_oper.cpp @@ -40,18 +40,18 @@ CmdResult CommandOper::HandleLocal(LocalUser* user, const Params& parameters) bool match_pass = false; bool match_hosts = false; - ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]); - if (i != ServerInstance->Config->oper_blocks.end()) + auto i = ServerInstance->Config->OperAccounts.find(parameters[0]); + if (i != ServerInstance->Config->OperAccounts.end()) { - std::shared_ptr ifo = i->second; - std::shared_ptr tag = ifo->oper_block; + std::shared_ptr ifo = i->second; + std::shared_ptr tag = ifo->GetConfig(); match_user = true; match_pass = ServerInstance->PassCompare(user, tag->getString("password"), parameters[1], tag->getString("hash")); match_hosts = InspIRCd::MatchMask(tag->getString("host"), user->MakeHost(), user->MakeHostIP()); if (match_pass && match_hosts) { - user->Oper(ifo); + user->OperLogin(ifo); return CmdResult::SUCCESS; } } diff --git a/src/coremods/core_oper/core_oper.cpp b/src/coremods/core_oper/core_oper.cpp index ba4e47575..0336b675a 100644 --- a/src/coremods/core_oper/core_oper.cpp +++ b/src/coremods/core_oper/core_oper.cpp @@ -54,17 +54,24 @@ public: cmdkill.hideservicekills = security->getBool("hideservicekills", security->getBool("hideulinekills")); } - void OnPostOper(User* user) override + void OnPostOperLogin(User* user) override { LocalUser* luser = IS_LOCAL(user); if (!luser) return; - const std::string vhost = luser->oper->getConfig("vhost"); + luser->WriteNumeric(RPL_YOUAREOPER, InspIRCd::Format("You are now %s %s", + user->oper->GetType()[0] ? "an" : "a", user->oper->GetType().c_str())); + + ServerInstance->SNO.WriteToSnoMask('o', "%s (%s) is now a server operator of type %s (using account %s)", + user->nick.c_str(), user->MakeHost().c_str(), user->oper->GetType().c_str(), + user->oper->GetName().c_str()); + + const std::string vhost = luser->oper->GetConfig()->getString("vhost"); if (!vhost.empty()) - luser->ChangeDisplayedHost(vhost); + user->ChangeDisplayedHost(vhost); - const std::string klass = luser->oper->getConfig("class"); + const std::string klass = luser->oper->GetConfig()->getString("class"); if (!klass.empty()) luser->SetClass(klass); } diff --git a/src/coremods/core_oper/core_oper.h b/src/coremods/core_oper/core_oper.h index a6ef6632c..6878b33b5 100644 --- a/src/coremods/core_oper/core_oper.h +++ b/src/coremods/core_oper/core_oper.h @@ -23,6 +23,13 @@ #include "inspircd.h" +enum +{ + // From RFC 1459. + RPL_YOUAREOPER = 381, + ERR_NOOPERHOST = 491, +}; + namespace DieRestart { /** Send an ERROR to partially connected users and a NOTICE to fully connected users diff --git a/src/coremods/core_oper/umode_o.cpp b/src/coremods/core_oper/umode_o.cpp index 0916f51d4..61b6cc56f 100644 --- a/src/coremods/core_oper/umode_o.cpp +++ b/src/coremods/core_oper/umode_o.cpp @@ -49,7 +49,7 @@ ModeAction ModeUserOperator::OnModeChange(User* source, User* dest, Channel*, Mo */ char snomask = IS_LOCAL(dest) ? 'o' : 'O'; ServerInstance->SNO.WriteToSnoMask(snomask, "User %s de-opered (by %s)", dest->nick.c_str(), source->nick.c_str()); - dest->UnOper(); + dest->OperLogout(); return MODEACTION_ALLOW; } diff --git a/src/coremods/core_oper/umode_s.cpp b/src/coremods/core_oper/umode_s.cpp index 1deb459de..70f409113 100644 --- a/src/coremods/core_oper/umode_s.cpp +++ b/src/coremods/core_oper/umode_s.cpp @@ -114,7 +114,7 @@ std::string ModeUserServerNoticeMask::ProcessNoticeMasks(User* user, const std:: else if (!user->HasSnomaskPermission(snomask)) { user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to snomask %c", - user->oper->name.c_str(), snomask)); + user->oper->GetType().c_str(), snomask)); continue; } } diff --git a/src/coremods/core_stats.cpp b/src/coremods/core_stats.cpp index 6d1111f4c..0208dbc8e 100644 --- a/src/coremods/core_stats.cpp +++ b/src/coremods/core_stats.cpp @@ -318,10 +318,9 @@ void CommandStats::DoStats(Stats::Context& stats) /* stats o */ case 'o': { - for (const auto& [_, ifo] : ServerInstance->Config->oper_blocks) + for (const auto& [_, ifo] : ServerInstance->Config->OperAccounts) { - std::shared_ptr tag = ifo->oper_block; - stats.AddRow(243, 'O', tag->getString("host"), '*', tag->getString("name"), tag->getString("type"), '0'); + stats.AddRow(243, 'O', ifo->GetConfig()->getString("host"), '*', ifo->GetName(), ifo->GetType(), '0'); } } break; @@ -329,23 +328,21 @@ void CommandStats::DoStats(Stats::Context& stats) { for (const auto& [_, tag] : ServerInstance->Config->OperTypes) { - tag->init(); - std::string umodes; for (const auto& [__, mh] : ServerInstance->Modes.GetModes(MODETYPE_USER)) { - if (mh->NeedsOper() && tag->AllowedUserModes[ModeParser::GetModeIndex(mh->GetModeChar())]) + if (mh->NeedsOper() && tag->CanUseMode(mh)) umodes.push_back(mh->GetModeChar()); } std::string cmodes; for (const auto& [__, mh] : ServerInstance->Modes.GetModes(MODETYPE_CHANNEL)) { - if (mh->NeedsOper() && tag->AllowedUserModes[ModeParser::GetModeIndex(mh->GetModeChar())]) + if (mh->NeedsOper() && tag->CanUseMode(mh)) cmodes.push_back(mh->GetModeChar()); } - stats.AddRow(243, 'O', tag->name, umodes, cmodes); + stats.AddRow(243, 'O', tag->GetName(), umodes, cmodes); } } break; diff --git a/src/coremods/core_whois.cpp b/src/coremods/core_whois.cpp index 496eb9a5b..23c0f90b2 100644 --- a/src/coremods/core_whois.cpp +++ b/src/coremods/core_whois.cpp @@ -228,7 +228,7 @@ void CommandWhois::DoWhois(LocalUser* user, User* dest, time_t signon, unsigned if (genericoper) whois.SendLine(RPL_WHOISOPERATOR, dest->server->IsService() ? "is a network service" : "is a server operator"); else - whois.SendLine(RPL_WHOISOPERATOR, InspIRCd::Format("is %s %s on %s", (strchr("AEIOUaeiou",dest->oper->name[0]) ? "an" : "a"), dest->oper->name.c_str(), ServerInstance->Config->Network.c_str())); + whois.SendLine(RPL_WHOISOPERATOR, InspIRCd::Format("is %s %s on %s", (strchr("AEIOUaeiou",dest->oper->GetType()[0]) ? "an" : "a"), dest->oper->GetType().c_str(), ServerInstance->Config->Network.c_str())); } if (whois.IsSelfWhois() || user->HasPrivPermission("users/auspex")) diff --git a/src/mode.cpp b/src/mode.cpp index f2db07af1..ba60cafbe 100644 --- a/src/mode.cpp +++ b/src/mode.cpp @@ -303,7 +303,7 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, Mode if (user->IsOper()) { user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to %sset %s mode %c", - user->oper->name.c_str(), mcitem.adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user", modechar)); + user->oper->GetType().c_str(), mcitem.adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user", modechar)); } else { diff --git a/src/modules.cpp b/src/modules.cpp index a2fcb3059..575c54211 100644 --- a/src/modules.cpp +++ b/src/modules.cpp @@ -106,9 +106,6 @@ void Module::OnPreRehash(User*, const std::string&) { DetachEvent(I_OnPreRehash void Module::OnModuleRehash(User*, const std::string&) { DetachEvent(I_OnModuleRehash); } ModResult Module::OnUserPreJoin(LocalUser*, Channel*, const std::string&, std::string&, const std::string&, bool) { DetachEvent(I_OnUserPreJoin); return MOD_RES_PASSTHRU; } void Module::OnMode(User*, User*, Channel*, const Modes::ChangeList&, ModeParser::ModeProcessFlag) { DetachEvent(I_OnMode); } -void Module::OnOper(User*) { DetachEvent(I_OnOper); } -void Module::OnPostOper(User*) { DetachEvent(I_OnPostOper); } -void Module::OnPostDeoper(User*) { DetachEvent(I_OnPostDeoper); } ModResult Module::OnUserPreInvite(User*, User*, Channel*, time_t) { DetachEvent(I_OnUserPreInvite); return MOD_RES_PASSTHRU; } ModResult Module::OnUserPreMessage(User*, const MessageTarget&, MessageDetails&) { DetachEvent(I_OnUserPreMessage); return MOD_RES_PASSTHRU; } ModResult Module::OnUserPreNick(LocalUser*, const std::string&) { DetachEvent(I_OnUserPreNick); return MOD_RES_PASSTHRU; } @@ -165,6 +162,11 @@ void Module::OnServiceAdd(ServiceProvider&) { DetachEvent(I_OnServiceAdd); } void Module::OnServiceDel(ServiceProvider&) { DetachEvent(I_OnServiceDel); } ModResult Module::OnUserWrite(LocalUser*, ClientProtocol::Message&) { DetachEvent(I_OnUserWrite); return MOD_RES_PASSTHRU; } void Module::OnShutdown(const std::string& reason) { DetachEvent(I_OnShutdown); } +ModResult Module::OnPreOperLogin(LocalUser*, const std::shared_ptr&) { DetachEvent(I_OnPreOperLogin); return MOD_RES_PASSTHRU; } +void Module::OnOperLogin(User*, const std::shared_ptr&) { DetachEvent(I_OnOperLogin); } +void Module::OnPostOperLogin(User*) { DetachEvent(I_OnPostOperLogin); } +void Module::OnOperLogout(User*) { DetachEvent(I_OnOperLogout); } +void Module::OnPostOperLogout(User*, const std::shared_ptr&) { DetachEvent(I_OnPostOperLogout); } ServiceProvider::ServiceProvider(Module* Creator, const std::string& Name, ServiceType Type) : creator(Creator), name(Name), service(Type) diff --git a/src/modules/m_check.cpp b/src/modules/m_check.cpp index c924f55e5..10ec6ca4b 100644 --- a/src/modules/m_check.cpp +++ b/src/modules/m_check.cpp @@ -134,6 +134,19 @@ class CommandCheck final return ret; } + static std::string GetAllowedOperOnlyCommands(LocalUser* user) + { + std::string ret; + for (const auto& [_, cmd] : ServerInstance->Parser.GetCommands()) + { + if (cmd->access_needed == CmdAccess::OPERATOR && user->HasCommandPermission(cmd->name)) + ret += cmd->name + " "; + } + if (!ret.empty()) + ret.pop_back(); + return ret; + } + static std::string GetAllowedOperOnlyModes(LocalUser* user, ModeType modetype) { std::string ret; @@ -209,15 +222,15 @@ public: if (targetuser->IsOper()) { - /* user is an oper of type ____ */ - context.Write("opertype", targetuser->oper->name); + context.Write("oper", targetuser->oper->GetName()); + context.Write("opertype", targetuser->oper->GetType()); if (localtarget) { context.Write("chanmodeperms", GetAllowedOperOnlyModes(localtarget, MODETYPE_CHANNEL)); context.Write("usermodeperms", GetAllowedOperOnlyModes(localtarget, MODETYPE_USER)); context.Write("snomaskperms", GetAllowedOperOnlySnomasks(localtarget)); - context.Write("commandperms", targetuser->oper->AllowedOperCommands.ToString()); - context.Write("permissions", targetuser->oper->AllowedPrivs.ToString()); + context.Write("commandperms", GetAllowedOperOnlyCommands(localtarget)); + context.Write("permissions", localtarget->oper->GetPrivileges()); } } diff --git a/src/modules/m_httpd_stats.cpp b/src/modules/m_httpd_stats.cpp index bcad9bf31..cb8b3a462 100644 --- a/src/modules/m_httpd_stats.cpp +++ b/src/modules/m_httpd_stats.cpp @@ -274,7 +274,7 @@ namespace Stats } if (u->IsOper()) - serializer.Attribute("opertype", u->oper->name); + serializer.Attribute("opertype", u->oper->GetType()); LocalUser* lu = IS_LOCAL(u); if (lu) diff --git a/src/modules/m_ldapoper.cpp b/src/modules/m_ldapoper.cpp index 78a235919..57e570d31 100644 --- a/src/modules/m_ldapoper.cpp +++ b/src/modules/m_ldapoper.cpp @@ -85,16 +85,16 @@ public: void OnResult(const LDAPResult& r) override { auto user = ServerInstance->Users.FindUUID(uid); - ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->oper_blocks.find(opername); + auto iter = ServerInstance->Config->OperAccounts.find(opername); - if (!user || iter == ServerInstance->Config->oper_blocks.end()) + if (!user || iter == ServerInstance->Config->OperAccounts.end()) { Fallback(); delete this; return; } - user->Oper(iter->second); + user->OperLogin(iter->second); delete this; } }; @@ -219,15 +219,11 @@ public: const std::string& opername = parameters[0]; const std::string& password = parameters[1]; - ServerConfig::OperIndex::const_iterator it = ServerInstance->Config->oper_blocks.find(opername); - if (it == ServerInstance->Config->oper_blocks.end()) + auto it = ServerInstance->Config->OperAccounts.find(opername); + if (it == ServerInstance->Config->OperAccounts.end()) return MOD_RES_PASSTHRU; - std::shared_ptr tag = it->second->oper_block; - if (!tag) - return MOD_RES_PASSTHRU; - - std::string acceptedhosts = tag->getString("host"); + std::string acceptedhosts = it->second->GetConfig()->getString("host"); if (!InspIRCd::MatchMask(acceptedhosts, user->MakeHost(), user->MakeHostIP())) return MOD_RES_PASSTHRU; diff --git a/src/modules/m_operchans.cpp b/src/modules/m_operchans.cpp index 61e430633..e6c53bb12 100644 --- a/src/modules/m_operchans.cpp +++ b/src/modules/m_operchans.cpp @@ -54,7 +54,7 @@ public: return false; // Replace spaces with underscores as they're prohibited in mode parameters. - std::string opername(user->oper->name); + std::string opername(user->oper->GetType()); stdalgo::string::replace_all(opername, space, underscore); return InspIRCd::Match(opername, text); } diff --git a/src/modules/m_operjoin.cpp b/src/modules/m_operjoin.cpp index e44224f4c..2608dcca3 100644 --- a/src/modules/m_operjoin.cpp +++ b/src/modules/m_operjoin.cpp @@ -52,7 +52,7 @@ public: operChans.push_back(channame); } - void OnPostOper(User* user) override + void OnPostOperLogin(User* user) override { LocalUser* localuser = IS_LOCAL(user); if (!localuser) @@ -64,7 +64,7 @@ public: Channel::JoinUser(localuser, operchan, override); } - irc::commasepstream ss(localuser->oper->getConfig("autojoin")); + irc::commasepstream ss(localuser->oper->GetConfig()->getString("autojoin")); for (std::string channame; ss.GetToken(channame); ) { if (ServerInstance->Channels.IsChannel(channame)) diff --git a/src/modules/m_operlevels.cpp b/src/modules/m_operlevels.cpp index 04c8e32e0..7b0c7a903 100644 --- a/src/modules/m_operlevels.cpp +++ b/src/modules/m_operlevels.cpp @@ -41,8 +41,8 @@ public: // oper killing an oper? if (dest->IsOper() && source->IsOper()) { - unsigned long dest_level = ConvToNum(dest->oper->getConfig("level")); - unsigned long source_level = ConvToNum(source->oper->getConfig("level")); + unsigned long dest_level = dest->oper->GetConfig()->getUInt("level", 0); + unsigned long source_level = source->oper->GetConfig()->getUInt("level", 0); if (dest_level > source_level) { diff --git a/src/modules/m_opermodes.cpp b/src/modules/m_opermodes.cpp index 2d7c39539..e0911986c 100644 --- a/src/modules/m_opermodes.cpp +++ b/src/modules/m_opermodes.cpp @@ -35,12 +35,12 @@ public: { } - void OnPostOper(User* user) override + void OnPostOperLogin(User* user) override { if (!IS_LOCAL(user)) return; // We don't handle remote users. - const std::string opermodes = user->oper->getConfig("modes"); + const std::string opermodes = user->oper->GetConfig()->getString("modes"); if (opermodes.empty()) return; // We don't have any modes to set. diff --git a/src/modules/m_opermotd.cpp b/src/modules/m_opermotd.cpp index 208496eae..c6cdd691d 100644 --- a/src/modules/m_opermotd.cpp +++ b/src/modules/m_opermotd.cpp @@ -93,7 +93,7 @@ public: { } - void OnOper(User* user) override + void OnPostOperLogin(User* user) override { if (onoper && IS_LOCAL(user)) cmd.ShowOperMOTD(user, false); diff --git a/src/modules/m_operprefix.cpp b/src/modules/m_operprefix.cpp index e44408f03..a3fae72e4 100644 --- a/src/modules/m_operprefix.cpp +++ b/src/modules/m_operprefix.cpp @@ -104,7 +104,7 @@ public: ServerInstance->Modes.Process(ServerInstance->FakeClient, memb->chan, nullptr, changelist); } - void OnPostOper(User* user) override + void OnPostOperLogin(User* user) override { if (IS_LOCAL(user) && (!user->IsModeSet(hideopermode))) SetOperPrefix(user, true); @@ -114,7 +114,7 @@ public: { // m_opermodes may set +H on the oper to hide him, we don't want to set the oper prefix in that case Module* opermodes = ServerInstance->Modules.Find("opermodes"); - ServerInstance->Modules.SetPriority(this, I_OnPostOper, PRIORITY_AFTER, opermodes); + ServerInstance->Modules.SetPriority(this, I_OnPostOperLogin, PRIORITY_AFTER, opermodes); } }; diff --git a/src/modules/m_override.cpp b/src/modules/m_override.cpp index f74453a65..348889de5 100644 --- a/src/modules/m_override.cpp +++ b/src/modules/m_override.cpp @@ -162,9 +162,7 @@ public: if (!source->IsModeSet(ou)) return false; - std::string tokenlist = source->oper->getConfig("override"); - // its defined or * is set, return its value as a boolean for if the token is set - return ((tokenlist.find(token, 0) != std::string::npos) || (tokenlist.find('*', 0) != std::string::npos)); + return TokenList(source->oper->GetConfig()->getString("override")).Contains(token); } ModResult OnPreTopicChange(User* source, Channel* channel, const std::string& topic) override diff --git a/src/modules/m_spanningtree/commands.h b/src/modules/m_spanningtree/commands.h index 8e8e31f94..3acbea5f6 100644 --- a/src/modules/m_spanningtree/commands.h +++ b/src/modules/m_spanningtree/commands.h @@ -139,7 +139,7 @@ public: : public CmdBuilder { public: - Builder(User* user); + Builder(User* user, const std::shared_ptr& oper); }; }; diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index efcac01c7..1cfaae9c7 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -505,7 +505,7 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user) CommandUID::Builder(user).Broadcast(); if (user->IsOper()) - CommandOpertype::Builder(user).Broadcast(); + CommandOpertype::Builder(user, user->oper).Broadcast(); for (const auto& [item, obj] : user->GetExtList()) { @@ -772,14 +772,14 @@ restart: } } -void ModuleSpanningTree::OnOper(User* user) +void ModuleSpanningTree::OnOperLogin(User* user, const std::shared_ptr& oper) { if (!user->IsFullyConnected() || !IS_LOCAL(user)) return; // Note: The protocol does not allow direct umode +o; // sending OPERTYPE infers +o modechange locally. - CommandOpertype::Builder(user).Broadcast(); + CommandOpertype::Builder(user, oper).Broadcast(); } void ModuleSpanningTree::OnAddLine(User* user, XLine* x) diff --git a/src/modules/m_spanningtree/main.h b/src/modules/m_spanningtree/main.h index dd2805557..539803a68 100644 --- a/src/modules/m_spanningtree/main.h +++ b/src/modules/m_spanningtree/main.h @@ -196,7 +196,7 @@ public: void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) override; void OnPreRehash(User* user, const std::string& parameter) override; void ReadConfig(ConfigStatus& status) override; - void OnOper(User* user) override; + void OnOperLogin(User* user, const std::shared_ptr& oper) override; void OnAddLine(User* u, XLine* x) override; void OnDelLine(User* u, XLine* x) override; ModResult OnStats(Stats::Context& stats) override; diff --git a/src/modules/m_spanningtree/netburst.cpp b/src/modules/m_spanningtree/netburst.cpp index 5dcc88504..4664111c2 100644 --- a/src/modules/m_spanningtree/netburst.cpp +++ b/src/modules/m_spanningtree/netburst.cpp @@ -282,7 +282,7 @@ void TreeSocket::SendUsers(BurstState& bs) this->WriteLine(CommandUID::Builder(user)); if (user->IsOper()) - this->WriteLine(CommandOpertype::Builder(user)); + this->WriteLine(CommandOpertype::Builder(user, user->oper)); if (user->IsAway()) this->WriteLine(CommandAway::Builder(user)); diff --git a/src/modules/m_spanningtree/opertype.cpp b/src/modules/m_spanningtree/opertype.cpp index 066913e0c..0dcfdc4d4 100644 --- a/src/modules/m_spanningtree/opertype.cpp +++ b/src/modules/m_spanningtree/opertype.cpp @@ -32,19 +32,11 @@ */ CmdResult CommandOpertype::HandleRemote(RemoteUser* u, CommandBase::Params& params) { - const std::string& opertype = params[0]; - if (!u->IsOper()) - ServerInstance->Users.all_opers.push_back(u); - - ModeHandler* opermh = ServerInstance->Modes.FindMode('o', MODETYPE_USER); - if (opermh) - u->SetMode(opermh, true); - - ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(opertype); - if (iter != ServerInstance->Config->OperTypes.end()) - u->oper = iter->second; + auto type = ServerInstance->Config->OperTypes.find(params[0]); + if (type != ServerInstance->Config->OperTypes.end()) + u->OperLogin(std::make_shared(type->first, type->second, ServerInstance->Config->EmptyTag)); else - u->oper = std::make_shared(opertype); + u->OperLogin(std::make_shared(params[0], nullptr, ServerInstance->Config->EmptyTag)); if (Utils->quiet_bursts) { @@ -57,13 +49,13 @@ CmdResult CommandOpertype::HandleRemote(RemoteUser* u, CommandBase::Params& para return CmdResult::SUCCESS; } - ServerInstance->SNO.WriteToSnoMask('O', "From %s: User %s (%s) is now a server operator of type %s", - u->server->GetName().c_str(), u->nick.c_str(), u->MakeHost().c_str(), opertype.c_str()); + ServerInstance->SNO.WriteToSnoMask('O', "From %s: %s (%s) is now a server operator of type %s", + u->server->GetName().c_str(), u->nick.c_str(), u->MakeHost().c_str(), u->oper->GetType().c_str()); return CmdResult::SUCCESS; } -CommandOpertype::Builder::Builder(User* user) +CommandOpertype::Builder::Builder(User* user, const std::shared_ptr& oper) : CmdBuilder(user, "OPERTYPE") { - push_last(user->oper->name); + push_last(oper->GetType()); } diff --git a/src/modules/m_sqloper.cpp b/src/modules/m_sqloper.cpp index 6323d1ef2..8eb295222 100644 --- a/src/modules/m_sqloper.cpp +++ b/src/modules/m_sqloper.cpp @@ -54,11 +54,9 @@ public: void OnResult(SQL::Result& res) override { - ServerConfig::OperIndex& oper_blocks = ServerInstance->Config->oper_blocks; - - // Remove our previous blocks from oper_blocks for a clean update + // Remove our previous blocks from the core for a clean update for (const auto& block : my_blocks) - oper_blocks.erase(block); + ServerInstance->Config->OperAccounts.erase(block); my_blocks.clear(); SQL::Row row; @@ -82,11 +80,11 @@ public: const std::string name = tag->getString("name"); // Skip both duplicate sqloper blocks and sqloper blocks that attempt to override conf blocks. - if (oper_blocks.find(name) != oper_blocks.end()) + if (ServerInstance->Config->OperAccounts.find(name) != ServerInstance->Config->OperAccounts.end()) continue; const std::string type = tag->getString("type"); - ServerConfig::OperIndex::iterator tblk = ServerInstance->Config->OperTypes.find(type); + auto tblk = ServerInstance->Config->OperTypes.find(type); if (tblk == ServerInstance->Config->OperTypes.end()) { ServerInstance->Logs.Normal(MODNAME, "Sqloper block " + name + " has missing type " + type); @@ -94,12 +92,7 @@ public: continue; } - auto ifo = std::make_shared(type); - - ifo->type_block = tblk->second->type_block; - ifo->oper_block = std::move(tag); - ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end()); - oper_blocks[name] = ifo; + ServerInstance->Config->OperAccounts[name] = std::make_shared(name, tblk->second, tag); my_blocks.push_back(name); row.clear(); } @@ -123,7 +116,7 @@ public: } } - // Call /oper after placing all blocks from the SQL table into the config->oper_blocks list. + // Call /oper after placing all blocks from the SQL table into the Config->OperAccounts list. void OperExec() { auto user = ServerInstance->Users.Find(uid); @@ -199,7 +192,7 @@ public: { // Remove all oper blocks that were from the DB for (const auto& block : my_blocks) - ServerInstance->Config->oper_blocks.erase(block); + ServerInstance->Config->OperAccounts.erase(block); } ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) override diff --git a/src/modules/m_sslinfo.cpp b/src/modules/m_sslinfo.cpp index 5366f4ddd..15b76e4f3 100644 --- a/src/modules/m_sslinfo.cpp +++ b/src/modules/m_sslinfo.cpp @@ -323,36 +323,16 @@ public: return MOD_RES_PASSTHRU; } - ModResult OnPreCommand(std::string& command, CommandBase::Params& parameters, LocalUser* user, bool validated) override + ModResult OnPreOperLogin(LocalUser* user, const std::shared_ptr& oper) override { - if ((command == "OPER") && (validated)) - { - ServerConfig::OperIndex::const_iterator i = ServerInstance->Config->oper_blocks.find(parameters[0]); - if (i != ServerInstance->Config->oper_blocks.end()) - { - std::shared_ptr ifo = i->second; - ssl_cert* cert = cmd.sslapi.GetCertificate(user); - - if (ifo->oper_block->getBool("sslonly") && !cert) - { - user->WriteNumeric(ERR_NOOPERHOST, "Invalid oper credentials"); - user->CommandFloodPenalty += 10000; - ServerInstance->SNO.WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': a secure connection is required.", user->GetFullRealHost().c_str(), parameters[0].c_str()); - return MOD_RES_DENY; - } - - std::string fingerprint; - if (ifo->oper_block->readString("fingerprint", fingerprint) && (!cert || !MatchFP(cert, fingerprint))) - { - user->WriteNumeric(ERR_NOOPERHOST, "Invalid oper credentials"); - user->CommandFloodPenalty += 10000; - ServerInstance->SNO.WriteGlobalSno('o', "WARNING! Failed oper attempt by %s using login '%s': their TLS client certificate fingerprint does not match.", user->GetFullRealHost().c_str(), parameters[0].c_str()); - return MOD_RES_DENY; - } - } - } + auto cert = cmd.sslapi.GetCertificate(user); + if (oper->GetConfig()->getBool("sslonly") && !cert) + return MOD_RES_DENY; + + const std::string fingerprint = oper->GetConfig()->getString("fingerprint"); + if (!fingerprint.empty() && (!cert || !MatchFP(cert, fingerprint))) + return MOD_RES_DENY; - // Let core handle it for extra stuff return MOD_RES_PASSTHRU; } @@ -382,19 +362,16 @@ public: return; // Find an auto-oper block for this user - for (const auto& [_, info] : ServerInstance->Config->oper_blocks) + for (const auto& [_, info] : ServerInstance->Config->OperAccounts) { - auto oper = info->oper_block; + const auto& oper = info->GetConfig(); if (!oper->getBool("autologin")) continue; // No autologin for this block. if (!InspIRCd::MatchMask(oper->getString("host"), localuser->MakeHost(), localuser->MakeHostIP())) continue; // Host doesn't match. - if (!MatchFP(cert, oper->getString("fingerprint"))) - continue; // Fingerprint doesn't match. - - user->Oper(info); + user->OperLogin(info); } } diff --git a/src/modules/m_swhois.cpp b/src/modules/m_swhois.cpp index 99f9007d0..06e101746 100644 --- a/src/modules/m_swhois.cpp +++ b/src/modules/m_swhois.cpp @@ -116,13 +116,12 @@ public: return MOD_RES_PASSTHRU; } - void OnPostOper(User* user) override + void OnPostOperLogin(User* user) override { if (!IS_LOCAL(user)) return; - std::string swhois = user->oper->getConfig("swhois"); - + const std::string swhois = user->oper->GetConfig()->getString("swhois"); if (!swhois.length()) return; @@ -130,7 +129,7 @@ public: cmd.swhois.Set(user, swhois); } - void OnPostDeoper(User* user) override + void OnPostOperLogout(User* user, const std::shared_ptr& oper) override { std::string* swhois = cmd.swhois.Get(user); if (!swhois) diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 041d32d3b..da40abd5c 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -301,7 +301,7 @@ void UserManager::QuitUser(User* user, const std::string& quitmessage, const std uuidlist.erase(user->uuid); user->PurgeEmptyChannels(); - user->UnOper(); + user->OperLogout(); } void UserManager::AddClone(User* user) diff --git a/src/users.cpp b/src/users.cpp index f72851f19..5f99b8d90 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -41,7 +41,6 @@ enum { // From RFC 1459. - RPL_YOUAREOPER = 381, ERR_NICKNAMEINUSE = 433, // From ircu. @@ -169,15 +168,7 @@ bool User::HasModePermission(const ModeHandler* mh) const bool LocalUser::HasModePermission(const ModeHandler* mh) const { - if (!this->IsOper()) - return false; - - const unsigned char mode = mh->GetModeChar(); - if (!ModeParser::IsModeChar(mode)) - return false; - - return ((mh->GetModeType() == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[ModeParser::GetModeIndex(mode)]; - + return IsOper() && oper->CanUseMode(mh); } /* * users on remote servers can completely bypass all permissions based checks. @@ -193,13 +184,7 @@ bool User::HasCommandPermission(const std::string& command) const bool LocalUser::HasCommandPermission(const std::string& command) const { - // are they even an oper at all? - if (!this->IsOper()) - { - return false; - } - - return oper->AllowedOperCommands.Contains(command); + return IsOper() && oper->CanUseCommand(command); } bool User::HasPrivPermission(const std::string& privstr) const @@ -209,10 +194,7 @@ bool User::HasPrivPermission(const std::string& privstr) const bool LocalUser::HasPrivPermission(const std::string& privstr) const { - if (!this->IsOper()) - return false; - - return oper->AllowedPrivs.Contains(privstr); + return IsOper() && oper->HasPrivilege(privstr); } bool User::HasSnomaskPermission(char chr) const @@ -222,10 +204,7 @@ bool User::HasSnomaskPermission(char chr) const bool LocalUser::HasSnomaskPermission(char chr) const { - if (!this->IsOper() || !ModeParser::IsModeChar(chr)) - return false; - - return this->oper->AllowedSnomasks[chr - 'A']; + return IsOper() && oper->CanUseSnomask(chr); } void UserIOHandler::OnDataReady() @@ -370,113 +349,80 @@ Cullable::Result FakeUser::Cull() return User::Cull(); } -void User::Oper(std::shared_ptr info) +bool User::OperLogin(const std::shared_ptr& account) { - ModeHandler* opermh = ServerInstance->Modes.FindMode('o', MODETYPE_USER); - if (opermh) - { - if (this->IsModeSet(opermh)) - this->UnOper(); - this->SetMode(opermh, true); - } - this->oper = info; - - LocalUser* localuser = IS_LOCAL(this); - if (localuser) + LocalUser* luser = IS_LOCAL(this); + if (luser && !quitting) { - Modes::ChangeList changelist; - changelist.push_add(opermh); - ClientProtocol::Events::Mode modemsg(ServerInstance->FakeClient, nullptr, localuser, changelist); - localuser->Send(modemsg); + ModResult modres; + FIRST_MOD_RESULT(OnPreOperLogin, modres, (luser, account)); + if (modres == MOD_RES_DENY) + return false; // Module rejected the oper attempt. } - FOREACH_MOD(OnOper, (this)); - - std::string opername; - if (info->oper_block) - opername = info->oper_block->getString("name"); + // If this user is already logged in to an oper account then log them out. + if (IsOper()) + OperLogout(); - ServerInstance->SNO.WriteToSnoMask('o', "%s (%s) is now a server operator of type %s (using oper '%s')", - nick.c_str(), MakeHost().c_str(), oper->name.c_str(), opername.c_str()); - this->WriteNumeric(RPL_YOUAREOPER, InspIRCd::Format("You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str())); + FOREACH_MOD(OnOperLogin, (this, account)); - ServerInstance->Users.all_opers.push_back(this); - - // Expand permissions from config for faster lookup - if (localuser) - oper->init(); - - FOREACH_MOD(OnPostOper, (this)); -} - -namespace -{ - void ParseModeList(ModeParser::ModeStatus& modeset, std::shared_ptr tag, const std::string& field) + // When a user logs in we need to: + // 1. Set the operator account (this is what IsOper checks). + // 2. Set user mode o (oper) *WITHOUT* calling the mode handler. + // 3. Add the user to the operator list. + oper = account; + auto opermh = ServerInstance->Modes.FindMode('o', MODETYPE_USER); + if (opermh) { - for (const auto& chr : tag->getString(field)) + SetMode(opermh, true); + if (luser) { - if (chr == '*') - modeset.set(); - else if (ModeParser::IsModeChar(chr)) - modeset.set(ModeParser::GetModeIndex(chr)); - else - ServerInstance->Logs.Normal("CONFIG", "'%c' is not a valid value for , ignoring...", chr, field.c_str()); + Modes::ChangeList changelist; + changelist.push_add(opermh); + ClientProtocol::Events::Mode modemsg(ServerInstance->FakeClient, nullptr, luser, changelist); + luser->Send(modemsg); } } -} - -void OperInfo::init() -{ - AllowedOperCommands.Clear(); - AllowedPrivs.Clear(); - AllowedUserModes.reset(); - AllowedChanModes.reset(); - AllowedSnomasks.reset(); + ServerInstance->Users.all_opers.push_back(this); - for (const auto& tag : class_blocks) - { - AllowedOperCommands.AddList(tag->getString("commands")); - AllowedPrivs.AddList(tag->getString("privs")); - ParseModeList(AllowedChanModes, tag, "chanmodes"); - ParseModeList(AllowedUserModes, tag, "usermodes"); - ParseModeList(AllowedSnomasks, tag, "snomasks"); - } + FOREACH_MOD(OnPostOperLogin, (this)); + return true; } -void User::UnOper() +void User::OperLogout() { - if (!this->IsOper()) + if (!IsOper()) return; - /* - * unset their oper type (what IS_OPER checks). - * note, order is important - this must come before modes as -o attempts - * to call UnOper. -- w00t - */ - oper = nullptr; + FOREACH_MOD(OnOperLogout, (this)); - // Remove the user from the oper list - stdalgo::vector::swaperase(ServerInstance->Users.all_opers, this); + // When a user logs OUT we need to: + // 1. Unset the operator account (this is what IsOper checks). + // 2. Unset user mode o (oper) *WITHOUT* calling the mode handler. + // 3. Remove the user from the operator list. + auto account = oper; + oper = nullptr; // If the user is quitting we shouldn't remove any modes as it results in // mode messages being broadcast across the network. - if (quitting) - return; - - /* Remove all oper only modes from the user when the deoper - Bug #466*/ - Modes::ChangeList changelist; - for (const auto& [_, mh] : ServerInstance->Modes.GetModes(MODETYPE_USER)) + if (!quitting) { - if (mh->NeedsOper()) - changelist.push_remove(mh); - } + // Remove any oper-only modes from the user. + Modes::ChangeList changelist; + for (const auto& [_, mh] : ServerInstance->Modes.GetModes(MODETYPE_USER)) + { + if (mh->NeedsOper() && IsModeSet(mh)) + changelist.push_remove(mh); + } + ServerInstance->Modes.Process(this, nullptr, this, changelist); - ServerInstance->Modes.Process(this, nullptr, this, changelist); + auto opermh = ServerInstance->Modes.FindMode('o', MODETYPE_USER); + if (opermh) + SetMode(opermh, false); + } - ModeHandler* opermh = ServerInstance->Modes.FindMode('o', MODETYPE_USER); - if (opermh) - this->SetMode(opermh, false); - FOREACH_MOD(OnPostDeoper, (this)); + stdalgo::vector::swaperase(ServerInstance->Users.all_opers, this); + FOREACH_MOD(OnPostOperLogout, (this, account)); } /* @@ -1305,3 +1251,105 @@ void ConnectClass::Update(const ConnectClass::Ptr src) type = src->type; uniqueusername = src->uniqueusername; } + +OperType::OperType(const std::string& n, const std::shared_ptr& t) + : config(std::make_shared("generated", FilePosition("", 0, 0))) + , name(n) +{ + if (t) + Configure(t, true); +} + +void OperType::Configure(const std::shared_ptr& tag, bool merge) +{ + commands.AddList(tag->getString("commands")); + privileges.AddList(tag->getString("privs")); + + auto modefn = [&tag](ModeParser::ModeStatus& modes, const std::string& key) + { + bool adding = true; + for (const auto& chr : tag->getString(key)) + { + if (chr == '+' || chr == '-') + adding = (chr == '+'); + else if (chr == '*' && adding) + modes.set(); + else if (chr == '*' && !adding) + modes.reset(); + else if (ModeParser::IsModeChar(chr)) + modes.set(ModeParser::GetModeIndex(chr), adding); + } + }; + modefn(chanmodes, "chanmodes"); + modefn(usermodes, "usermodes"); + modefn(snomasks, "snomasks"); + + if (merge) + MergeTag(tag); +} + +bool OperType::CanUseCommand(const std::string& cmd) const +{ + return commands.Contains(cmd); +} + +bool OperType::CanUseMode(const ModeHandler* mh) const +{ + const size_t index = ModeParser::GetModeIndex(mh->GetModeChar()); + if (index == ModeParser::MODEID_MAX) + return false; + + return (mh->GetModeType() == MODETYPE_USER ? usermodes : chanmodes)[index]; +} + +bool OperType::CanUseSnomask(unsigned char chr) const +{ + if ((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z')) + return snomasks[chr - 'A']; + return false; +} + +bool OperType::HasPrivilege(const std::string& priv) const +{ + return privileges.Contains(priv); +} + +void OperType::MergeTag(const std::shared_ptr& tag) +{ + for (const auto& [key, value] : tag->GetItems()) + { + if (stdalgo::string::equalsci(key, "name") || stdalgo::string::equalsci(key, "type") + || stdalgo::string::equalsci(key, "classes")) + { + // These are meta keys for linking the oper/type/class tags. + continue; + } + + if (stdalgo::string::equalsci(key, "commands") || stdalgo::string::equalsci(key, "privs") + || stdalgo::string::equalsci(key, "chanmodes") || stdalgo::string::equalsci(key, "usermodes") + || stdalgo::string::equalsci(key, "snomasks")) + { + // These have already been parsed into the object. + continue; + } + + config->GetItems()[key] = value; + } +} + +OperAccount::OperAccount(const std::string& n, const std::shared_ptr& o, const std::shared_ptr& t) + : OperType(n, nullptr) + , type(o ? o->GetName() : n) +{ + if (o) + { + chanmodes = o->chanmodes; + commands = o->commands; + privileges = o->privileges; + snomasks = o->snomasks; + usermodes = o->usermodes; + Configure(o->GetConfig(), true); + } + Configure(t, true); + +} -- cgit v1.3.1-10-gc9f91