diff options
| author | 2026-03-12 15:15:38 +0000 | |
|---|---|---|
| committer | 2026-03-12 15:38:54 +0000 | |
| commit | 7ed7c7ba0dfd3f73a7d8a621988d7e99f07a23ef (patch) | |
| tree | 1f98a809b4018a6cabfe17aa2549507072840438 /modules | |
| parent | Move channel settings to the <channels> tag. (diff) | |
Move <options:defaultmodes> to <channels> and rework.
- The default prefix modes are now separate from the default channel
modes. This should result in less accidentally broken configs.
- The privs are now pre-parsed to a list of mode references. This
should improve performance slightly as the repeated mode lookups
are gone. It also allows us to write warnings to the debug log
when the default privs are invalid.
- Channels can now have a default topic.
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/account.cpp | 2 | ||||
| -rw-r--r-- | modules/cban.cpp | 2 | ||||
| -rw-r--r-- | modules/core/core_channel/core_channel.cpp | 99 | ||||
| -rw-r--r-- | modules/denychans.cpp | 2 | ||||
| -rw-r--r-- | modules/helpmode.cpp | 30 | ||||
| -rw-r--r-- | modules/joinflood.cpp | 2 | ||||
| -rw-r--r-- | modules/kicknorejoin.cpp | 2 | ||||
| -rw-r--r-- | modules/ojoin.cpp | 8 | ||||
| -rw-r--r-- | modules/operchans.cpp | 2 | ||||
| -rw-r--r-- | modules/operprefix.cpp | 4 | ||||
| -rw-r--r-- | modules/override.cpp | 2 | ||||
| -rw-r--r-- | modules/redirect.cpp | 2 | ||||
| -rw-r--r-- | modules/restrictchans.cpp | 2 | ||||
| -rw-r--r-- | modules/spanningtree/ijoin.cpp | 18 | ||||
| -rw-r--r-- | modules/sslmodes.cpp | 2 |
15 files changed, 149 insertions, 30 deletions
diff --git a/modules/account.cpp b/modules/account.cpp index 1d6d416b2..491c25fa2 100644 --- a/modules/account.cpp +++ b/modules/account.cpp @@ -283,7 +283,7 @@ public: return HandleMessage(user, target); } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (override) return MOD_RES_PASSTHRU; diff --git a/modules/cban.cpp b/modules/cban.cpp index c0e69c9fb..02cfbb6ef 100644 --- a/modules/cban.cpp +++ b/modules/cban.cpp @@ -198,7 +198,7 @@ public: return MOD_RES_DENY; } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (override) return MOD_RES_PASSTHRU; diff --git a/modules/core/core_channel/core_channel.cpp b/modules/core/core_channel/core_channel.cpp index 844a752cf..c5351be87 100644 --- a/modules/core/core_channel/core_channel.cpp +++ b/modules/core/core_channel/core_channel.cpp @@ -129,6 +129,17 @@ class CoreModChannel final insp::flat_map<std::string, char> exemptions; ExtBanManager extbanmgr; + // <channels:defaultmodes> + using DefaultModeList = std::vector<std::pair<ChanModeReference, std::string>>; + DefaultModeList defaultmodes; + + // <channels:defaultprivs> + using DefaultPrivList = std::vector<ChanModeReference>; + DefaultPrivList defaultprivs; + + // <channels:defaulttopic> + std::string defaulttopic; + ModResult IsInvited(User* user, Channel* chan) { auto* localuser = user->AsLocal(); @@ -191,6 +202,59 @@ public: { "letter", ExtBan::Format::LETTER }, }); + + DefaultPrivList newdefaultprivs; + { + for (const auto modechr : channelstag->getString("defaultprivs", "o")) + { + if (modechr == '+') + continue; // This is implied. + + auto* pm = ServerInstance->Modes.FindPrefixMode(modechr); + if (!pm) + { + ServerInstance->Logs.Debug(MODNAME, "Ignoring invalid mode in <channels:defaultprivs>: {}", + modechr); + continue; + } + + ChanModeReference moderef(this, pm->service_name); + newdefaultprivs.push_back(moderef); + } + } + + DefaultModeList newdefaultmodes; + { + irc::spacesepstream defaultmodestream(channelstag->getString("defaultmodes", "nt")); + + std::string modestr; + defaultmodestream.GetToken(modestr); + for (const auto modechr : modestr) + { + if (modechr == '+') + continue; // This is implied. + + auto* mh = ServerInstance->Modes.FindMode(modechr, MODETYPE_CHANNEL); + if (!mh || mh->IsPrefixMode()) + { + ServerInstance->Logs.Debug(MODNAME, "Ignoring invalid mode in <channels:defaultmodes>: {}", + modechr); + continue; + } + + std::string modeparam; + if (mh->NeedsParam(true) && (!defaultmodestream.GetToken(modeparam) || modeparam.empty())) + { + ServerInstance->Logs.Debug(MODNAME, "Ignoring mode with missing parameter in <channels:defaultmodes>: {} ({})", + modechr, mh->service_name); + continue; + } + + ChanModeReference moderef(this, mh->service_name); + newdefaultmodes.emplace_back(std::make_pair(moderef, modeparam)); + } + } + const auto& securitytag = ServerInstance->Config->ConfValue("security"); Invite::AnnounceState newannouncestate = securitytag->getEnum("announceinvites", Invite::ANNOUNCE_DYNAMIC, { { "all", Invite::ANNOUNCE_ALL }, @@ -204,10 +268,14 @@ public: // Validates and applies <maxlist> tags, so do it first banmode.DoRehash(); + defaultprivs.swap(newdefaultprivs); + defaultmodes.swap(newdefaultmodes); exemptions.swap(exempts); + extbanmgr.format = newformat; invapi.announceinvites = newannouncestate; joinhook.modefromuser = channelstag->getBool("cyclehostsfromuser"); + defaulttopic = channelstag->getString("defaulttopic"); Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckList }; if (channelstag->getBool("invitebypassmodes", true)) @@ -251,7 +319,7 @@ public: tokens["CHANLIMIT"] = FMT::format("#:{}", opermaxchans); } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (override) { @@ -276,7 +344,14 @@ public: } if (!chan) + { + for (auto& defaultpriv : defaultprivs) + { + if (defaultpriv && defaultpriv->IsPrefixMode()) + privs.insert(defaultpriv->IsPrefixMode()); + } return MOD_RES_PASSTHRU; // Can't have limits on a new channel. + } // Check whether the channel key is correct. const std::string ckey = chan->GetModeParameter(&keymode); @@ -327,6 +402,28 @@ public: return MOD_RES_PASSTHRU; } + void OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts) override + { + if (!created_by_local) + return; + + for (auto& [mh, mparam] : this->defaultmodes) + { + if (!mh || mh->IsPrefixMode()) + continue; // Not settable here. + + if (mh->NeedsParam(true) && mparam.empty()) + continue; // Parameter missing. + + Modes::Change modechange(*mh, true, mparam); + mh->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, memb->chan, modechange); + } + + if (!this->defaulttopic.empty()) + memb->chan->SetTopic(ServerInstance->FakeClient, this->defaulttopic, ServerInstance->Time()); + } + + void OnPostJoin(Membership* memb) override { Channel* const chan = memb->chan; diff --git a/modules/denychans.cpp b/modules/denychans.cpp index 5986097ed..fc1cab9db 100644 --- a/modules/denychans.cpp +++ b/modules/denychans.cpp @@ -144,7 +144,7 @@ public: goodchannels.swap(goodchans); } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (override) return MOD_RES_PASSTHRU; diff --git a/modules/helpmode.cpp b/modules/helpmode.cpp index 6bca93ba5..635905129 100644 --- a/modules/helpmode.cpp +++ b/modules/helpmode.cpp @@ -57,8 +57,10 @@ private: HelpOp helpop; UserModeReference hideoper; bool markhelpers; - std::string helpchanmodes; - insp::flat_map<std::string, std::string> helpchans; + + // <helpchan:prefix> + using HelpChanMap = insp::flat_map<std::string, std::vector<ChanModeReference>>; + HelpChanMap helpchans; public: ModuleHelpMode() @@ -72,14 +74,22 @@ public: void ReadConfig(ConfigStatus& status) override { + HelpChanMap newhelpchans; for (const auto& [_, tag] : ServerInstance->Config->ConfTags("helpchan")) { const auto name = tag->getString("name"); if (name.empty()) throw ModuleException(this, "<helpchan:name> must not be empty at " + tag->source.str()); - helpchans[name] = tag->getString("prefix", "o", 1); + auto& newhelpchan = newhelpchans[name]; + for (const auto modechr : tag->getString("prefix", "o", 1)) + { + auto* mh = ServerInstance->Modes.FindPrefixMode(modechr); + if (mh) + newhelpchan.push_back(ChanModeReference(this, mh->service_name)); + } } + std::swap(helpchans, newhelpchans); const auto& tag = ServerInstance->Config->ConfValue("helpmode"); ignorehideoper = tag->getBool("ignorehideoper"); @@ -125,18 +135,22 @@ public: return MOD_RES_PASSTHRU; } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (!user->IsModeSet(helpop)) return MOD_RES_PASSTHRU; - for (const auto& [helpchan, prefix] : helpchans) + for (auto& [helpchan, prefixes] : helpchans) { - if (InspIRCd::Match(cname, helpchan)) + if (!InspIRCd::Match(cname, helpchan)) + continue; + + for (auto& prefix : prefixes) { - privs.append(prefix); - break; + if (prefix && prefix->IsPrefixMode()) + privs.insert(prefix->IsPrefixMode()); } + break; } return MOD_RES_PASSTHRU; } diff --git a/modules/joinflood.cpp b/modules/joinflood.cpp index ce61b1f82..f5d8596d9 100644 --- a/modules/joinflood.cpp +++ b/modules/joinflood.cpp @@ -169,7 +169,7 @@ public: ignoreuntil = std::max<time_t>(ignoreuntil, ServerInstance->Time() + splitwait); } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (!override && chan) { diff --git a/modules/kicknorejoin.cpp b/modules/kicknorejoin.cpp index ba5a81c2f..01d45fa73 100644 --- a/modules/kicknorejoin.cpp +++ b/modules/kicknorejoin.cpp @@ -137,7 +137,7 @@ public: { } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (!override && chan) { diff --git a/modules/ojoin.cpp b/modules/ojoin.cpp index 2c2e163b6..8c6abced3 100644 --- a/modules/ojoin.cpp +++ b/modules/ojoin.cpp @@ -120,13 +120,13 @@ public: { } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (mycommand.active) { - privs += np.GetModeChar(); - if (mycommand.op && mycommand.opmode) - privs += mycommand.opmode->IsPrefixMode()->GetPrefix(); + privs.insert(&np); + if (mycommand.op && mycommand.opmode && mycommand.opmode->IsPrefixMode()) + privs.insert(mycommand.opmode->IsPrefixMode()); return MOD_RES_ALLOW; } diff --git a/modules/operchans.cpp b/modules/operchans.cpp index 5c9663f93..882773813 100644 --- a/modules/operchans.cpp +++ b/modules/operchans.cpp @@ -92,7 +92,7 @@ public: { } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (!override && chan && chan->IsModeSet(oc) && !user->IsOper()) { diff --git a/modules/operprefix.cpp b/modules/operprefix.cpp index d54e04e83..e122b8572 100644 --- a/modules/operprefix.cpp +++ b/modules/operprefix.cpp @@ -75,10 +75,10 @@ public: */ } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if ((user->IsOper()) && (!user->IsModeSet(hideopermode))) - privs.push_back('y'); + privs.insert(&opm); return MOD_RES_PASSTHRU; } diff --git a/modules/override.cpp b/modules/override.cpp index 466dc59a9..c7ef26d88 100644 --- a/modules/override.cpp +++ b/modules/override.cpp @@ -235,7 +235,7 @@ public: return MOD_RES_PASSTHRU; } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (user->IsOper() && !override) { diff --git a/modules/redirect.cpp b/modules/redirect.cpp index 27ba422dc..2b59534b5 100644 --- a/modules/redirect.cpp +++ b/modules/redirect.cpp @@ -258,7 +258,7 @@ public: action_limit = tag->getBool("limit", true); } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (override || !chan) return MOD_RES_PASSTHRU; // No redirect possible. diff --git a/modules/restrictchans.cpp b/modules/restrictchans.cpp index 346dcfc2d..7d4320317 100644 --- a/modules/restrictchans.cpp +++ b/modules/restrictchans.cpp @@ -78,7 +78,7 @@ public: allowregistered = tag->getBool("allowregistered", false); } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { // channel does not yet exist (record is null, about to be created IF we were to allow it) if (!override && !chan && !CanCreateChannel(user, cname)) diff --git a/modules/spanningtree/ijoin.cpp b/modules/spanningtree/ijoin.cpp index 01d5f75e5..6072d293c 100644 --- a/modules/spanningtree/ijoin.cpp +++ b/modules/spanningtree/ijoin.cpp @@ -40,17 +40,25 @@ CmdResult CommandIJoin::HandleRemote(RemoteUser* user, Params& params) return CmdResult::FAILURE; } - bool apply_modes; + auto apply_modes = false; + PrefixMode::Set modes; if (params.size() > 4) { time_t RemoteTS = ServerCommand::ExtractTS(params[3]); - apply_modes = (RemoteTS <= chan->age); + if (RemoteTS <= chan->age) + { + apply_modes = true; + for (const auto modechr : params[4]) + { + auto* pm = ServerInstance->Modes.FindPrefixMode(modechr); + if (pm) + modes.insert(pm); + } + } } - else - apply_modes = false; // Join the user and set the membership id to what they sent - Membership* memb = chan->ForceJoin(user, apply_modes ? ¶ms[4] : nullptr); + auto* memb = chan->ForceJoin(user, apply_modes ? &modes : nullptr); if (!memb) return CmdResult::FAILURE; diff --git a/modules/sslmodes.cpp b/modules/sslmodes.cpp index fdb138747..ea478e609 100644 --- a/modules/sslmodes.cpp +++ b/modules/sslmodes.cpp @@ -196,7 +196,7 @@ public: extban.operonly = tag->getBool("operonly"); } - ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, std::string& privs, const std::string& keygiven, bool override) override + ModResult OnUserPreJoin(LocalUser* user, Channel* chan, const std::string& cname, PrefixMode::Set& privs, const std::string& keygiven, bool override) override { if (override) return MOD_RES_PASSTHRU; // Allow override joins. |
