diff options
Diffstat (limited to 'modules/chanserv/cs_clone.cpp')
-rw-r--r-- | modules/chanserv/cs_clone.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/modules/chanserv/cs_clone.cpp b/modules/chanserv/cs_clone.cpp new file mode 100644 index 000000000..79db9fd57 --- /dev/null +++ b/modules/chanserv/cs_clone.cpp @@ -0,0 +1,262 @@ +/* ChanServ core functions + * + * (C) 2003-2024 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + */ + +#include "module.h" +#include "modules/bs_badwords.h" + +class CommandCSClone final + : public Command +{ + void CopySetting(ChannelInfo *ci, ChannelInfo *target_ci, const Anope::string &setting) + { + if (ci->HasExt(setting)) + target_ci->Extend<bool>(setting); + } + + void CopyAccess(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci) + { + std::set<Anope::string> masks; + unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024"); + unsigned count = 0; + + for (unsigned i = 0; i < target_ci->GetAccessCount(); ++i) + masks.insert(target_ci->GetAccess(i)->Mask()); + + for (unsigned i = 0; i < ci->GetAccessCount(); ++i) + { + const ChanAccess *taccess = ci->GetAccess(i); + AccessProvider *provider = taccess->provider; + + if (access_max && target_ci->GetDeepAccessCount() >= access_max) + break; + + if (masks.count(taccess->Mask())) + continue; + masks.insert(taccess->Mask()); + + ChanAccess *newaccess = provider->Create(); + newaccess->SetMask(taccess->Mask(), target_ci); + newaccess->creator = taccess->creator; + newaccess->description = taccess->description; + newaccess->last_seen = taccess->last_seen; + newaccess->created = taccess->created; + newaccess->AccessUnserialize(taccess->AccessSerialize()); + + target_ci->AddAccess(newaccess); + + ++count; + } + + source.Reply(_("%d access entries from \002%s\002 have been cloned to \002%s\002."), count, ci->name.c_str(), target_ci->name.c_str()); + } + + void CopyAkick(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci) + { + target_ci->ClearAkick(); + for (unsigned i = 0; i < ci->GetAkickCount(); ++i) + { + const AutoKick *akick = ci->GetAkick(i); + if (akick->nc) + target_ci->AddAkick(akick->creator, akick->nc, akick->reason, akick->addtime, akick->last_used); + else + target_ci->AddAkick(akick->creator, akick->mask, akick->reason, akick->addtime, akick->last_used); + } + + source.Reply(_("All akick entries from \002%s\002 have been cloned to \002%s\002."), ci->name.c_str(), target_ci->name.c_str()); + } + + void CopyBadwords(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci) + { + BadWords *target_badwords = target_ci->Require<BadWords>("badwords"), + *badwords = ci->Require<BadWords>("badwords"); + + if (!target_badwords || !badwords) + { + source.Reply(ACCESS_DENIED); // BotServ doesn't exist/badwords isn't loaded + return; + } + + target_badwords->ClearBadWords(); + + for (unsigned i = 0; i < badwords->GetBadWordCount(); ++i) + { + const BadWord *bw = badwords->GetBadWord(i); + target_badwords->AddBadWord(bw->word, bw->type); + } + + badwords->Check(); + target_badwords->Check(); + + source.Reply(_("All badword entries from \002%s\002 have been cloned to \002%s\002."), ci->name.c_str(), target_ci->name.c_str()); + } + + void CopyLevels(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci) + { + for (const auto &[priv, level] : ci->GetLevelEntries()) + { + target_ci->SetLevel(priv, level); + } + + source.Reply(_("All level entries from \002%s\002 have been cloned into \002%s\002."), ci->name.c_str(), target_ci->name.c_str()); + } + +public: + CommandCSClone(Module *creator) : Command(creator, "chanserv/clone", 2, 3) + { + this->SetDesc(_("Copy all settings from one channel to another")); + this->SetSyntax(_("\037channel\037 \037target\037 [\037what\037]")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + const Anope::string &channel = params[0]; + const Anope::string &target = params[1]; + Anope::string what = params.size() > 2 ? params[2] : ""; + + if (Anope::ReadOnly) + { + source.Reply(READ_ONLY_MODE); + return; + } + + User *u = source.GetUser(); + ChannelInfo *ci = ChannelInfo::Find(channel); + bool override = false; + + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, channel.c_str()); + return; + } + + ChannelInfo *target_ci = ChannelInfo::Find(target); + if (!target_ci) + { + source.Reply(CHAN_X_NOT_REGISTERED, target.c_str()); + return; + } + + if (ci == target_ci) + { + source.Reply(_("Cannot clone channel \002%s\002 to itself!"), target.c_str()); + return; + } + + if (!source.IsFounder(ci) || !source.IsFounder(target_ci)) + { + if (!source.HasPriv("chanserv/administration")) + { + source.Reply(ACCESS_DENIED); + return; + } + else + override = true; + } + + if (what.equals_ci("ALL")) + what.clear(); + + if (what.empty()) + { + delete target_ci; + target_ci = new ChannelInfo(*ci); + target_ci->name = target; + target_ci->time_registered = Anope::CurTime; + (*RegisteredChannelList)[target_ci->name] = target_ci; + target_ci->c = Channel::Find(target_ci->name); + + target_ci->bi = NULL; + if (ci->bi) + ci->bi->Assign(u, target_ci); + + if (target_ci->c) + { + target_ci->c->ci = target_ci; + + target_ci->c->CheckModes(); + + target_ci->c->SetCorrectModes(u, true); + } + + if (target_ci->c && !target_ci->c->topic.empty()) + { + target_ci->last_topic = target_ci->c->topic; + target_ci->last_topic_setter = target_ci->c->topic_setter; + target_ci->last_topic_time = target_ci->c->topic_time; + } + else + target_ci->last_topic_setter = source.service->nick; + + const Anope::string settings[] = { "NOAUTOOP", "CS_KEEP_MODES", "PEACE", "PERSIST", "RESTRICTED", + "CS_SECURE", "SECUREFOUNDER", "SECUREOPS", "SIGNKICK", "SIGNKICK_LEVEL", "CS_NO_EXPIRE" }; + + for (const auto &setting : settings) + CopySetting(ci, target_ci, setting); + + CopyAccess(source, ci, target_ci); + CopyAkick(source, ci, target_ci); + CopyBadwords(source, ci, target_ci); + CopyLevels(source, ci, target_ci); + + FOREACH_MOD(OnChanRegistered, (target_ci)); + + source.Reply(_("All settings from \002%s\002 have been cloned to \002%s\002."), ci->name.c_str(), target_ci->name.c_str()); + } + else if (what.equals_ci("ACCESS")) + { + CopyAccess(source, ci, target_ci); + } + else if (what.equals_ci("AKICK")) + { + CopyAkick(source, ci, target_ci); + } + else if (what.equals_ci("BADWORDS")) + { + CopyBadwords(source, ci, target_ci); + } + else if (what.equals_ci("LEVELS")) + { + CopyLevels(source, ci, target_ci); + } + else + { + this->OnSyntaxError(source, ""); + return; + } + + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clone " << (what.empty() ? "everything from it" : what) << " to " << target_ci->name; + } + + bool OnHelp(CommandSource &source, const Anope::string &subcommand) override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Copies all settings, access, akicks, etc from \002channel\002 to the\n" + "\002target\002 channel. If \037what\037 is \002ACCESS\002, \002AKICK\002, \002BADWORDS\002,\n" + "or \002LEVELS\002 then only the respective settings are cloned.\n" + "You must be the founder of \037channel\037 and \037target\037.")); + return true; + } +}; + +class CSClone final + : public Module +{ + CommandCSClone commandcsclone; + +public: + CSClone(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcsclone(this) + { + + } +}; + +MODULE_INIT(CSClone) |