aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorGravatar Sadie Powell2026-03-12 15:15:38 +0000
committerGravatar Sadie Powell2026-03-12 15:38:54 +0000
commit7ed7c7ba0dfd3f73a7d8a621988d7e99f07a23ef (patch)
tree1f98a809b4018a6cabfe17aa2549507072840438 /modules
parentMove 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.cpp2
-rw-r--r--modules/cban.cpp2
-rw-r--r--modules/core/core_channel/core_channel.cpp99
-rw-r--r--modules/denychans.cpp2
-rw-r--r--modules/helpmode.cpp30
-rw-r--r--modules/joinflood.cpp2
-rw-r--r--modules/kicknorejoin.cpp2
-rw-r--r--modules/ojoin.cpp8
-rw-r--r--modules/operchans.cpp2
-rw-r--r--modules/operprefix.cpp4
-rw-r--r--modules/override.cpp2
-rw-r--r--modules/redirect.cpp2
-rw-r--r--modules/restrictchans.cpp2
-rw-r--r--modules/spanningtree/ijoin.cpp18
-rw-r--r--modules/sslmodes.cpp2
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 ? &params[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.