From 010beb52b1f3c697a07f9a130d2ed9335fe1cd98 Mon Sep 17 00:00:00 2001 From: Sadie Powell Date: Sat, 3 May 2025 17:07:07 +0100 Subject: Store the setter and ts for all modes and try to restore them. This is mostly for preserving channel list mode info. --- modules/botserv/botserv.cpp | 6 ++--- modules/chanserv/chanserv.cpp | 6 ++--- modules/chanserv/cs_mode.cpp | 2 +- modules/chanserv/cs_set.cpp | 47 +++++++++++++++++++++++++++-------- modules/chanstats.cpp | 2 +- modules/database/db_atheme.cpp | 8 +++--- modules/helpchan.cpp | 4 +-- modules/irc2sql/irc2sql.cpp | 4 +-- modules/irc2sql/irc2sql.h | 2 +- modules/nickserv/ns_set_keepmodes.cpp | 45 ++++++++++++++++++++++++++------- modules/operserv/os_defcon.cpp | 4 +-- modules/operserv/os_mode.cpp | 4 +-- modules/protocol/inspircd.cpp | 47 ++++++++++++++++++++++++++++++++--- modules/rpc/rpc_data.cpp | 12 ++++----- 14 files changed, 143 insertions(+), 50 deletions(-) (limited to 'modules') diff --git a/modules/botserv/botserv.cpp b/modules/botserv/botserv.cpp index 99e2c9134..2b251b67b 100644 --- a/modules/botserv/botserv.cpp +++ b/modules/botserv/botserv.cpp @@ -196,15 +196,15 @@ public: } } - EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string ¶m) override + EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const ModeData &data) override { if (source.GetUser() && !source.GetBot() && Config->GetModule(this).Get("smartjoin") && mode->name == "BAN" && c->ci && c->ci->bi && c->FindUser(c->ci->bi)) { BotInfo *bi = c->ci->bi; - Entry ban("BAN", param); + Entry ban("BAN", data.value); if (ban.Matches(bi)) - c->RemoveMode(bi, "BAN", param); + c->RemoveMode(bi, "BAN", data.value); } return EVENT_CONTINUE; diff --git a/modules/chanserv/chanserv.cpp b/modules/chanserv/chanserv.cpp index 8f3ec68e4..259a92842 100644 --- a/modules/chanserv/chanserv.cpp +++ b/modules/chanserv/chanserv.cpp @@ -309,7 +309,7 @@ public: return; if (c->ci) - c->SetMode(c->ci->WhoSends(), "REGISTERED", "", false); + c->SetMode(c->ci->WhoSends(), "REGISTERED", {}, false); else c->RemoveMode(c->WhoSends(), "REGISTERED", "", false); @@ -461,7 +461,7 @@ public: } } - EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) override + EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const ModeData &data) override { if (!always_lower && Anope::CurTime == c->created && c->ci && setter.GetUser() && !setter.GetUser()->server->IsULined()) { @@ -470,7 +470,7 @@ public: if (cu && cm && !cu->status.HasMode(cm->mchar)) { /* Our -o and their mode change crossing, bounce their mode */ - c->RemoveMode(c->ci->WhoSends(), mode, param); + c->RemoveMode(c->ci->WhoSends(), mode, data.value); /* We don't set mlocks until after the join has finished processing, it will stack with this change, * so there isn't much for the user to remove except -nt etc which is likely locked anyway. */ diff --git a/modules/chanserv/cs_mode.cpp b/modules/chanserv/cs_mode.cpp index 26741654f..d6339311e 100644 --- a/modules/chanserv/cs_mode.cpp +++ b/modules/chanserv/cs_mode.cpp @@ -1008,7 +1008,7 @@ public: if (cm->type == MODE_REGULAR) { if (!c->HasMode(cm->name) && ml->set) - c->SetMode(NULL, cm, "", false); + c->SetMode(NULL, cm, {}, false); else if (c->HasMode(cm->name) && !ml->set) c->RemoveMode(NULL, cm, "", false); } diff --git a/modules/chanserv/cs_set.cpp b/modules/chanserv/cs_set.cpp index d3236ea77..8761a3c47 100644 --- a/modules/chanserv/cs_set.cpp +++ b/modules/chanserv/cs_set.cpp @@ -1121,13 +1121,19 @@ class CSSet final const ChannelInfo *ci = anope_dynamic_static_cast(s); Anope::string modes; - for (const auto &[last_mode, last_value] : ci->last_modes) + for (const auto &[last_mode, last_data] : ci->last_modes) { if (!modes.empty()) modes += " "; + + modes += '+'; modes += last_mode; - if (!last_value.empty()) - modes += "," + last_value; + if (!last_data.value.empty()) + { + modes += "," + Anope::ToString(last_data.set_at); + modes += "," + last_data.set_by; + modes += "," + last_data.value; + } } data.Store("last_modes", modes); } @@ -1145,11 +1151,32 @@ class CSSet final ci->last_modes.clear(); for (spacesepstream sep(modes); sep.GetToken(modes);) { - size_t c = modes.find(','); - if (c == Anope::string::npos) - ci->last_modes.emplace(modes, ""); + if (modes[0] == '+') + { + commasepstream mode(modes, true); + mode.GetToken(modes); + modes.erase(0, 1); + + ModeData info; + Anope::string set_at; + mode.GetToken(set_at); + info.set_at = Anope::Convert(set_at, 0); + mode.GetToken(info.set_by); + info.value = mode.GetRemaining(); + + ci->last_modes.emplace(modes, info); + continue; + } else - ci->last_modes.emplace(modes.substr(0, c), modes.substr(c + 1)); + { + // Begin 2.0 compatibility + size_t c = modes.find(','); + if (c == Anope::string::npos) + ci->last_modes.emplace(modes, ModeData()); + else + ci->last_modes.emplace(modes.substr(0, c), ModeData(modes.substr(c + 1))); + // End 2.0 compatibility. + } } } } keep_modes; @@ -1207,8 +1234,8 @@ public: if (c->ci && keep_modes.HasExt(c->ci)) { Channel::ModeList ml = c->ci->last_modes; - for (const auto &[last_mode, last_value] : ml) - c->SetMode(c->ci->WhoSends(), last_mode, last_value); + for (const auto &[last_mode, last_data] : ml) + c->SetMode(c->ci->WhoSends(), last_mode, last_data); } } @@ -1230,7 +1257,7 @@ public: persist.Unset(ci); } - EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) override + EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const ModeData &data) override { if (c->ci) { diff --git a/modules/chanstats.cpp b/modules/chanstats.cpp index 97a03b013..2f48c8abf 100644 --- a/modules/chanstats.cpp +++ b/modules/chanstats.cpp @@ -540,7 +540,7 @@ public: this->RunQuery(query); } - EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) override + EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const ModeData &data) override { this->OnModeChange(c, setter.GetUser()); return EVENT_CONTINUE; diff --git a/modules/database/db_atheme.cpp b/modules/database/db_atheme.cpp index 950432d10..971a45068 100644 --- a/modules/database/db_atheme.cpp +++ b/modules/database/db_atheme.cpp @@ -84,14 +84,14 @@ public: } }; -struct ModeData final +struct ModeLockData final { char letter; Anope::string name; Anope::string value; bool set; - ModeData(const Anope::string &n, bool s, const Anope::string &v = "") + ModeLockData(const Anope::string &n, bool s, const Anope::string &v = "") : letter(0) , name(n) , value(v) @@ -99,7 +99,7 @@ struct ModeData final { } - ModeData(char l, const Anope::string &v = "") + ModeLockData(char l, const Anope::string &v = "") : letter(l) , value(v) , set(true) @@ -123,7 +123,7 @@ struct ChannelData final Anope::string info_adder; Anope::string info_message; time_t info_ts = 0; - std::vector mlocks; + std::vector mlocks; Anope::string suspend_by; Anope::string suspend_reason; time_t suspend_ts = 0; diff --git a/modules/helpchan.cpp b/modules/helpchan.cpp index 1854b78c7..80e4c1a30 100644 --- a/modules/helpchan.cpp +++ b/modules/helpchan.cpp @@ -16,11 +16,11 @@ public: { } - EventReturn OnChannelModeSet(Channel *c, MessageSource &, ChannelMode *mode, const Anope::string ¶m) override + EventReturn OnChannelModeSet(Channel *c, MessageSource &, ChannelMode *mode, const ModeData &data) override { if (mode->name == "OP" && c && c->ci && c->name.equals_ci(Config->GetModule(this).Get("helpchannel"))) { - User *u = User::Find(param); + User *u = User::Find(data.value); if (u && c->ci->AccessFor(u).HasPriv("OPME")) u->SetMode(Config->GetClient("OperServ"), "HELPOP"); diff --git a/modules/irc2sql/irc2sql.cpp b/modules/irc2sql/irc2sql.cpp index fc402dc95..629a749d2 100644 --- a/modules/irc2sql/irc2sql.cpp +++ b/modules/irc2sql/irc2sql.cpp @@ -232,11 +232,11 @@ void IRC2SQL::OnJoinChannel(User *u, Channel *c) this->RunQuery(query); } -EventReturn IRC2SQL::OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) +EventReturn IRC2SQL::OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const ModeData &data) { if (mode->type == MODE_STATUS) { - User *u = User::Find(param); + User *u = User::Find(data.value); if (u == NULL) return EVENT_CONTINUE; diff --git a/modules/irc2sql/irc2sql.h b/modules/irc2sql/irc2sql.h index 52a15e7f6..95d01cc4a 100644 --- a/modules/irc2sql/irc2sql.h +++ b/modules/irc2sql/irc2sql.h @@ -79,7 +79,7 @@ public: void OnChannelDelete(Channel *c) override; void OnLeaveChannel(User *u, Channel *c) override; void OnJoinChannel(User *u, Channel *c) override; - EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) override; + EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const ModeData &data) override; EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) override; void OnTopicUpdated(User *source, Channel *c, const Anope::string &user, const Anope::string &topic) override; diff --git a/modules/nickserv/ns_set_keepmodes.cpp b/modules/nickserv/ns_set_keepmodes.cpp index faa44b9d3..8e5c902b2 100644 --- a/modules/nickserv/ns_set_keepmodes.cpp +++ b/modules/nickserv/ns_set_keepmodes.cpp @@ -128,13 +128,19 @@ private: const NickCore *nc = anope_dynamic_static_cast(s); Anope::string modes; - for (const auto &[last_mode, last_value] : nc->last_modes) + for (const auto &[last_mode, last_data] : nc->last_modes) { if (!modes.empty()) modes += " "; + + modes += '+'; modes += last_mode; - if (!last_value.empty()) - modes += "," + last_value; + if (!last_data.value.empty()) + { + modes += "," + Anope::ToString(last_data.set_at); + modes += "," + last_data.set_by; + modes += "," + last_data.value; + } } data.Store("last_modes", modes); } @@ -152,11 +158,32 @@ private: nc->last_modes.clear(); for (spacesepstream sep(modes); sep.GetToken(modes);) { - size_t c = modes.find(','); - if (c == Anope::string::npos) - nc->last_modes.emplace(modes, ""); + if (modes[0] == '+') + { + commasepstream mode(modes, true); + mode.GetToken(modes); + modes.erase(0, 1); + + ModeData info; + Anope::string set_at; + mode.GetToken(set_at); + info.set_at = Anope::Convert(set_at, 0); + mode.GetToken(info.set_by); + info.value = mode.GetRemaining(); + + nc->last_modes.emplace(modes, info); + continue; + } else - nc->last_modes.emplace(modes.substr(0, c), modes.substr(c + 1)); + { + // Begin 2.0 compatibility + size_t c = modes.find(','); + if (c == Anope::string::npos) + nc->last_modes.emplace(modes, ModeData()); + else + nc->last_modes.emplace(modes.substr(0, c), ModeData(modes.substr(c + 1))); + // End 2.0 compatibility. + } } } } keep_modes; @@ -197,11 +224,11 @@ public: { const auto norestore = Config->GetModule(this).Get("norestore"); User::ModeList modes = u->Account()->last_modes; - for (const auto &[last_mode, last_value] : modes) + for (const auto &[last_mode, last_data] : modes) { auto *um = ModeManager::FindUserModeByName(last_mode); if (um && um->CanSet(nullptr) && norestore.find(um->mchar) == Anope::string::npos) - u->SetMode(nullptr, last_mode, last_value); + u->SetMode(nullptr, last_mode, last_data); } } } diff --git a/modules/operserv/os_defcon.cpp b/modules/operserv/os_defcon.cpp index 9fd47c659..2d32e6a28 100644 --- a/modules/operserv/os_defcon.cpp +++ b/modules/operserv/os_defcon.cpp @@ -407,11 +407,11 @@ public: this->ParseModeString(); } - EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const Anope::string ¶m) override + EventReturn OnChannelModeSet(Channel *c, MessageSource &source, ChannelMode *mode, const ModeData &data) override { if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOff.count(mode->name) && source.GetUser() && !source.GetBot()) { - c->RemoveMode(Config->GetClient("OperServ"), mode, param); + c->RemoveMode(Config->GetClient("OperServ"), mode, data.value); return EVENT_STOP; } diff --git a/modules/operserv/os_mode.cpp b/modules/operserv/os_mode.cpp index 0ac9b48d4..d623d2e71 100644 --- a/modules/operserv/os_mode.cpp +++ b/modules/operserv/os_mode.cpp @@ -36,8 +36,8 @@ public: { bool all = params.size() > 2 && params[2].equals_ci("ALL"); - for (const auto &[mode, value] : c->GetModes()) - c->RemoveMode(c->WhoSends(), mode, value, false); + for (const auto &[mode, data] : c->GetModes()) + c->RemoveMode(c->WhoSends(), mode, data.value, false); if (!c) { diff --git a/modules/protocol/inspircd.cpp b/modules/protocol/inspircd.cpp index 6665a4581..684c32efe 100644 --- a/modules/protocol/inspircd.cpp +++ b/modules/protocol/inspircd.cpp @@ -434,6 +434,41 @@ public: Uplink::SendInternal({}, Me, "NUM", newparams); } + void SendMode(const MessageSource &source, Channel *chan, const ModeManager::Change &change) + { + std::map> listchanges; + ModeManager::Change otherchanges; + + for (const auto &[mode, info] : change) + { + if (spanningtree_proto_ver >= 1206 && mode->type == MODE_LIST && info.first) + { + // Adding to a list mode. + const auto &data = info.second; + + auto &listchange = listchanges[mode->mchar]; + listchange.push_back(data.value); + listchange.push_back(data.set_by); + listchange.push_back(Anope::ToString(data.set_at)); + } + else + { + // Regular mode change or mode removal. + otherchanges.emplace(mode, info); + } + } + + for (auto &[mode, params] : listchanges) + { + // : LMODE [ ]+ + params.insert(params.begin(), { chan->name, Anope::ToString(chan->created), Anope::ToString(mode) }); + Uplink::SendInternal({}, source, "LMODE", params); + } + + if (!otherchanges.empty()) + IRCDProto::SendMode(source, chan, otherchanges); + } + void SendModeInternal(const MessageSource &source, Channel *chan, const Anope::string &modes, const std::vector &values) override { auto params = values; @@ -2310,7 +2345,7 @@ struct IRCDMessageLMode final void Run(MessageSource &source, const std::vector ¶ms, const Anope::map &tags) override { - // : LMODE [ ]+ + // : LMODE [ ]+ auto *chan = Channel::Find(params[0]); if (!chan) return; // Channel doesn't exist. @@ -2327,10 +2362,14 @@ struct IRCDMessageLMode final if (params.size() % 3) return; // Invalid parameter count. - for (auto it = params.begin() + 3; it != params.end(); it += 3) + for (auto it = params.begin() + 3; it != params.end(); ) { - // TODO: Anope doesn't store set time and setter for list modes yet. - chan->SetModeInternal(source, cm, *it); + ModeData data; + data.value = *it++; + data.set_by = *it++; + data.set_at = Anope::Convert(*it++, 0); + + chan->SetModeInternal(source, cm, data, true); } } }; diff --git a/modules/rpc/rpc_data.cpp b/modules/rpc/rpc_data.cpp index 71c0fee71..13dec3cf2 100644 --- a/modules/rpc/rpc_data.cpp +++ b/modules/rpc/rpc_data.cpp @@ -220,15 +220,15 @@ public: } std::vector modelist = { "+" }; - for (const auto &[mname, mvalue] : c->GetModes()) + for (const auto &[mname, mdata] : c->GetModes()) { auto *cm = ModeManager::FindChannelModeByName(mname); if (!cm || cm->type == MODE_LIST) continue; modelist.front().push_back(cm->mchar); - if (!mvalue.empty()) - modelist.push_back(mvalue); + if (!mdata.value.empty()) + modelist.push_back(mdata.value); } auto &modes = root.ReplyArray("modes"); for (const auto &modeparam : modelist) @@ -522,15 +522,15 @@ public: root.Reply("fingerprint", u->fingerprint); std::vector modelist = { "+" }; - for (const auto &[mname, mvalue] : u->GetModeList()) + for (const auto &[mname, mdata] : u->GetModeList()) { auto *um = ModeManager::FindUserModeByName(mname); if (!um || um->type == MODE_LIST) continue; modelist.front().push_back(um->mchar); - if (!mvalue.empty()) - modelist.push_back(mvalue); + if (!mdata.value.empty()) + modelist.push_back(mdata.value); } auto &modes = root.ReplyArray("modes"); for (const auto &modeparam : modelist) -- cgit