1
0
mirror of https://github.com/anope/anope.git synced 2026-06-12 15:44:46 +02:00
Files
anope/modules/chanserv/cs_clone.cpp
T
2026-01-01 18:07:12 +00:00

275 lines
7.7 KiB
C++

// Anope IRC Services <https://www.anope.org/>
//
// Copyright (C) 2003-2026 Anope Contributors
//
// Anope is free software. You can use, modify, and/or distribute it under the
// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt
// for the complete terms of this license and docs/AUTHORS.txt for a list of
// contributors.
//
// Based on the original code of Epona by Lara
// Based on the original code of Services by Andy Church
//
// SPDX-License-Identifier: GPL-2.0-only
#include "module.h"
#include "modules/botserv/badwords.h"
#include "modules/chanserv/akick.h"
class CommandCSClone final
: public Command
{
static void CopySetting(ChannelInfo *ci, ChannelInfo *target_ci, const Anope::string &setting)
{
if (ci->HasExt(setting))
target_ci->Extend<bool>(setting);
}
static void CopyAccess(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
{
std::set<Anope::string> masks;
unsigned access_max = Config->GetModule("chanserv").Get<unsigned>("accessmax", "1000");
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());
}
static void CopyAkick(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
{
if (!ChanServ::akick_service)
{
source.Reply(TRY_AGAIN_LATER, source.command.nobreak().c_str());
return;
}
ChanServ::akick_service->ClearAKick(target_ci);
for (unsigned i = 0; i < ChanServ::akick_service->GetAKickCount(ci); ++i)
{
const auto *akick = ChanServ::akick_service->GetAKick(ci, i);
if (akick->nc)
ChanServ::akick_service->AddAKick(ci, akick->creator, akick->nc, akick->reason, akick->addtime, akick->last_used);
else
ChanServ::akick_service->AddAKick(ci, 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());
}
static void CopyBadwords(CommandSource &source, ChannelInfo *ci, ChannelInfo *target_ci)
{
auto *target_badwords = target_ci->Require<BotServ::BadWords>(BOTSERV_BAD_WORDS_EXT);
auto *badwords = ci->Require<BotServ::BadWords>(BOTSERV_BAD_WORDS_EXT);
if (!target_badwords || !badwords)
{
source.Reply(TRY_AGAIN_LATER, source.command.nobreak().c_str());
return;
}
target_badwords->ClearBadWords();
for (unsigned i = 0; i < badwords->GetBadWordCount(); ++i)
{
const auto *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());
}
static 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> &params) 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->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",
"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 "
"\002target\002 channel. If \037what\037 is \002ACCESS\002, \002AKICK\002, \002BADWORDS\002, "
"or \002LEVELS\002 then only the respective settings are cloned. "
"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)