summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/example.conf2
-rw-r--r--docs/Changes.conf1
-rw-r--r--include/language.h7
-rw-r--r--include/regchannel.h5
-rw-r--r--modules/core/cs_clone.cpp181
-rw-r--r--modules/core/cs_register.cpp14
-rw-r--r--src/channels.cpp4
-rw-r--r--src/language.cpp19
-rw-r--r--src/regchannel.cpp47
9 files changed, 271 insertions, 9 deletions
diff --git a/data/example.conf b/data/example.conf
index 4655875ca..aa6964e16 100644
--- a/data/example.conf
+++ b/data/example.conf
@@ -1044,7 +1044,7 @@ chanserv
* The core modules to load for ChanServ. This is a space separated list that corresponds
* to the base names of the modules for ChanServ. This directive is optional, but highly recommended.
*/
- modules = "cs_help cs_register cs_set cs_saset cs_saset_noexpire cs_set_bantype cs_set_description cs_set_entrymsg cs_set_founder cs_set_keeptopic cs_set_mlock cs_set_opnotice cs_set_peace cs_set_persist cs_set_private cs_set_restricted cs_set_secure cs_set_securefounder cs_set_secureops cs_set_signkick cs_set_successor cs_set_topiclock cs_set_xop cs_xop cs_access cs_akick cs_drop cs_ban cs_clear cs_modes cs_getkey cs_invite cs_kick cs_list cs_topic cs_info cs_forbid cs_suspend cs_status cs_unban"
+ modules = "cs_help cs_register cs_set cs_saset cs_saset_noexpire cs_set_bantype cs_set_description cs_set_entrymsg cs_set_founder cs_set_keeptopic cs_set_mlock cs_set_opnotice cs_set_peace cs_set_persist cs_set_private cs_set_restricted cs_set_secure cs_set_securefounder cs_set_secureops cs_set_signkick cs_set_successor cs_set_topiclock cs_set_xop cs_xop cs_access cs_akick cs_drop cs_ban cs_clear cs_modes cs_getkey cs_invite cs_kick cs_list cs_topic cs_info cs_forbid cs_suspend cs_status cs_unban cs_clone"
/*
* The default options for newly registered channels. Note that changing these options
diff --git a/docs/Changes.conf b/docs/Changes.conf
index b453fec76..792477f12 100644
--- a/docs/Changes.conf
+++ b/docs/Changes.conf
@@ -1,6 +1,7 @@
Anope Version 1.9.4
-------------------
memoserv:modules added ms_ignore
+chanserv:modules added cs_clone
Anope Version 1.9.3
------------------
diff --git a/include/language.h b/include/language.h
index 51f3e1cfc..5406e3480 100644
--- a/include/language.h
+++ b/include/language.h
@@ -568,6 +568,11 @@ enum LanguageString
CHAN_CLEARED_OPS,
CHAN_CLEARED_USERS,
CHAN_CLEARED_INVITES,
+ CHAN_CLONED,
+ CHAN_CLONED_ACCESS,
+ CHAN_CLONED_AKICK,
+ CHAN_CLONED_BADWORDS,
+ CHAN_CLONE_SYNTAX,
CHAN_GETKEY_SYNTAX,
CHAN_GETKEY_NOKEY,
CHAN_GETKEY_KEY,
@@ -1287,6 +1292,7 @@ enum LanguageString
CHAN_HELP_CMD_OWNER,
CHAN_HELP_CMD_PROTECT,
CHAN_HELP_CMD_DEOP,
+ CHAN_HELP_CMD_CLONE,
CHAN_HELP,
CHAN_HELP_EXPIRES,
CHAN_HELP_REGISTER,
@@ -1362,6 +1368,7 @@ enum LanguageString
CHAN_HELP_TOPIC,
CHAN_HELP_CLEAR,
CHAN_HELP_GETKEY,
+ CHAN_HELP_CLONE,
CHAN_SERVADMIN_HELP,
CHAN_SERVADMIN_HELP_DROP,
CHAN_SERVADMIN_HELP_SET_NOEXPIRE,
diff --git a/include/regchannel.h b/include/regchannel.h
index 689857c2d..1300b9cf4 100644
--- a/include/regchannel.h
+++ b/include/regchannel.h
@@ -78,6 +78,11 @@ class CoreExport ChannelInfo : public Extensible, public Flags<ChannelInfoFlag,
*/
ChannelInfo(const Anope::string &chname);
+ /** Copy constructor
+ * @param ci The ChannelInfo to copy settings to
+ */
+ ChannelInfo(ChannelInfo *ci);
+
/** Default destructor
*/
~ChannelInfo();
diff --git a/modules/core/cs_clone.cpp b/modules/core/cs_clone.cpp
new file mode 100644
index 000000000..a6568290c
--- /dev/null
+++ b/modules/core/cs_clone.cpp
@@ -0,0 +1,181 @@
+/* ChanServ core functions
+ *
+ * (C) 2003-2010 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"
+
+class CommandCSClone : public Command
+{
+public:
+ CommandCSClone() : Command("CLONE", 2, 3)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<Anope::string> &params)
+ {
+ const Anope::string &channel = params[0];
+ const Anope::string &target = params[1];
+ Anope::string what = params.size() > 2 ? params[2] : "";
+
+ ChannelInfo *ci = cs_findchan(channel);
+ if (!check_access(u, ci, CA_SET))
+ {
+ u->SendMessage(ChanServ, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ ChannelInfo *target_ci = cs_findchan(target);
+ if (!target_ci)
+ {
+ u->SendMessage(ChanServ, CHAN_X_NOT_REGISTERED, target.c_str());
+ return MOD_CONT;
+ }
+ if (!IsFounder(u, ci) || !IsFounder(u, target_ci))
+ {
+ u->SendMessage(ChanServ, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (Config->CSMaxReg && u->Account()->channelcount >= Config->CSMaxReg && !u->Account()->HasPriv("chanserv/no-register-limit"))
+ {
+ u->SendMessage(ChanServ, u->Account()->channelcount > Config->CSMaxReg ? CHAN_EXCEEDED_CHANNEL_LIMIT : CHAN_REACHED_CHANNEL_LIMIT, Config->CSMaxReg);
+ return MOD_CONT;
+ }
+
+ if (what.equals_ci("ALL"))
+ what.clear();
+
+ if (what.empty())
+ {
+ delete target_ci;
+ target_ci = new ChannelInfo(ci);
+ target_ci->name = target;
+ RegisteredChannelList[target_ci->name] = target_ci;
+ target_ci->c = findchan(target_ci->name);
+ if (target_ci->c)
+ {
+ target_ci->c->ci = target_ci;
+
+ check_modes(target_ci->c);
+
+ ChannelMode *cm;
+ if (u->FindChannel(target_ci->c) != NULL)
+ {
+ /* On most ircds you do not receive the admin/owner mode till its registered */
+ if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER)))
+ target_ci->c->SetMode(NULL, cm, u->nick);
+ else if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT)))
+ target_ci->c->RemoveMode(NULL, cm, u->nick);
+ }
+
+ /* Mark the channel as persistant */
+ if (target_ci->c->HasMode(CMODE_PERM))
+ target_ci->SetFlag(CI_PERSIST);
+ /* Persist may be in def cflags, set it here */
+ else if (target_ci->HasFlag(CI_PERSIST) && (cm = ModeManager::FindChannelModeByName(CMODE_PERM)))
+ target_ci->c->SetMode(NULL, CMODE_PERM);
+
+ if (target_ci->bi && target_ci->c->FindUser(target_ci->bi) == NULL)
+ target_ci->bi->Join(target_ci->c);
+ }
+
+ 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 = Config->s_ChanServ;
+
+ FOREACH_MOD(I_OnChanRegistered, OnChanRegistered(target_ci));
+
+ u->SendMessage(ChanServ, CHAN_CLONED, channel.c_str(), target.c_str());
+ }
+ else if (what.equals_ci("ACCESS"))
+ {
+ target_ci->ClearAccess();
+ for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
+ {
+ ChanAccess *access = ci->GetAccess(i);
+ target_ci->AddAccess(access->nc, access->level, access->creator, access->last_seen);
+ }
+
+ u->SendMessage(ChanServ, CHAN_CLONED_ACCESS, channel.c_str(), target.c_str());
+ }
+ else if (what.equals_ci("AKICK"))
+ {
+ target_ci->ClearAkick();
+ for (unsigned i = 0; i < ci->GetAkickCount(); ++i)
+ {
+ AutoKick *akick = ci->GetAkick(i);
+ if (akick->HasFlag(AK_ISNICK))
+ 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);
+ }
+
+ u->SendMessage(ChanServ, CHAN_CLONED_AKICK, channel.c_str(), target.c_str());
+ }
+ else if (what.equals_ci("BADWORDS"))
+ {
+ target_ci->ClearBadWords();
+ for (unsigned i = 0; i < ci->GetBadWordCount(); ++i)
+ {
+ BadWord *bw = ci->GetBadWord(i);
+ target_ci->AddBadWord(bw->word, bw->type);
+ }
+
+ u->SendMessage(ChanServ, CHAN_CLONED_BADWORDS, channel.c_str(), target.c_str());
+ }
+ else
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ Log(LOG_COMMAND, u, this, ci) << "to clone it to " << target_ci->name;
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const Anope::string &subcommand)
+ {
+ u->SendMessage(ChanServ, CHAN_HELP_CLONE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const Anope::string &subcommand)
+ {
+ SyntaxError(ChanServ, u, "CLONE", CHAN_CLONE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ u->SendMessage(ChanServ, CHAN_HELP_CMD_CLONE);
+ }
+};
+
+class CSClone : public Module
+{
+ CommandCSClone commandcsclone;
+
+ public:
+ CSClone(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, &commandcsclone);
+ }
+};
+
+MODULE_INIT(CSClone)
diff --git a/modules/core/cs_register.cpp b/modules/core/cs_register.cpp
index c488bfc4a..bef71a726 100644
--- a/modules/core/cs_register.cpp
+++ b/modules/core/cs_register.cpp
@@ -71,11 +71,15 @@ class CommandCSRegister : public Command
if (c)
{
check_modes(c);
- /* On most ircds you do not receive the admin/owner mode till its registered */
- if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER)))
- c->SetMode(NULL, cm, u->nick);
- else if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT)))
- c->RemoveMode(NULL, cm, u->nick);
+
+ if (u->FindChannel(c) != NULL)
+ {
+ /* On most ircds you do not receive the admin/owner mode till its registered */
+ if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER)))
+ c->SetMode(NULL, cm, u->nick);
+ else if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT)))
+ c->RemoveMode(NULL, cm, u->nick);
+ }
/* Mark the channel as persistant */
if (c->HasMode(CMODE_PERM))
diff --git a/src/channels.cpp b/src/channels.cpp
index 6e1e25f23..72e573029 100644
--- a/src/channels.cpp
+++ b/src/channels.cpp
@@ -566,7 +566,7 @@ void Channel::SetMode(BotInfo *bi, ChannelMode *cm, const Anope::string &param,
else if (cm->Type == MODE_STATUS)
{
User *u = finduser(param);
- if (u && HasUserStatus(u, debug_cast<ChannelModeStatus *>(cm)))
+ if (!u || HasUserStatus(u, debug_cast<ChannelModeStatus *>(cm)))
return;
}
else if (cm->Type == MODE_LIST)
@@ -607,7 +607,7 @@ void Channel::RemoveMode(BotInfo *bi, ChannelMode *cm, const Anope::string &para
else if (cm->Type == MODE_STATUS)
{
User *u = finduser(param);
- if (u && !HasUserStatus(u, debug_cast<ChannelModeStatus *>(cm)))
+ if (!u || !HasUserStatus(u, debug_cast<ChannelModeStatus *>(cm)))
return;
}
else if (cm->Type == MODE_LIST)
diff --git a/src/language.cpp b/src/language.cpp
index a49508d0c..4a89ac626 100644
--- a/src/language.cpp
+++ b/src/language.cpp
@@ -1322,6 +1322,16 @@ const char *const language_strings[LANG_STRING_COUNT] = {
_("All users have been kicked from channel %s."),
/* CHAN_CLEARED_INVITES */
_("All invites on channel %s have been removed."),
+ /* CHAN_CLONED */
+ _("All settings from \002%s\002 have been transferred to \002%s\002"),
+ /* CHAN_CLONED_ACCESS */
+ _("All access entries from \002%s\002 have been transferred to \002%s\002"),
+ /* CHAN_CLONED_AKICK */
+ _("All akick entries from \002%s\002 have been transferred to \002%s\002"),
+ /* CHAN_CLONED_BADWORDS */
+ _("All badword entries from \002%s\002 have been transferred to \002%s\002"),
+ /* CHAN_CLONE_SYNTAX */
+ _("CLONE \037channel\037 \037target\037"),
/* CHAN_GETKEY_SYNTAX */
_("GETKEY channel"),
/* CHAN_GETKEY_NOKEY */
@@ -3354,6 +3364,8 @@ const char *const language_strings[LANG_STRING_COUNT] = {
_(" PROTECT Protects a selected nick on a channel"),
/* CHAN_HELP_CMD_DEOP */
_(" DEOP Deops a selected nick on a channel"),
+ /* CHAN_HELP_CMD_CLONE */
+ _(" CLONE Copy all settings from one channel to another"),
/* CHAN_HELP */
_("%S allows you to register and control various\n"
"aspects of channels. %S can often prevent\n"
@@ -4131,6 +4143,13 @@ const char *const language_strings[LANG_STRING_COUNT] = {
_("Syntax: GETKEY channel\n"
" \n"
"Returns the key of the given channel."),
+ /* CHAN_HELP_CLONE */
+ _("Syntax: \002CLONE \037channel\037 \037target\037 [all | access | akick | badwords]\002\n"
+ " \n"
+ "Copies all settings, access, akicks, etc from channel to the\n"
+ "target channel. If access, akick, or badwords is specified then only\n"
+ "the respective settings are transferred. You must have founder level\n"
+ "access to \037channel\037 and \037target\037."),
/* CHAN_SERVADMIN_HELP */
_(" \n"
"Services Operators can also drop any channel without needing\n"
diff --git a/src/regchannel.cpp b/src/regchannel.cpp
index b4cded05b..b4c42cb82 100644
--- a/src/regchannel.cpp
+++ b/src/regchannel.cpp
@@ -52,7 +52,7 @@ ChannelInfo::ChannelInfo(const Anope::string &chname)
this->memos.memomax = Config->MSMaxMemos;
this->last_used = this->time_registered = Anope::CurTime;
- this->ttb = new int16[2 * TTB_SIZE];
+ this->ttb = new int16[TTB_SIZE];
for (int i = 0; i < TTB_SIZE; ++i)
this->ttb[i] = 0;
@@ -61,6 +61,51 @@ ChannelInfo::ChannelInfo(const Anope::string &chname)
RegisteredChannelList[this->name] = this;
}
+/** Copy constructor
+ * @param ci The ChannelInfo to copy settings to
+ */
+ChannelInfo::ChannelInfo(ChannelInfo *ci)
+{
+ *this = *ci;
+
+ if (this->founder)
+ ++this->founder->channelcount;
+
+ this->access.clear();
+ this->akick.clear();
+ this->badwords.clear();
+
+ if (this->bi)
+ ++this->bi->chancount;
+
+ this->ttb = new int16[TTB_SIZE];
+ for (int i = 0; i < TTB_SIZE; ++i)
+ this->ttb[i] = ci->ttb[i];
+
+ this->levels = new int16[CA_SIZE];
+ for (int i = 0; i < CA_SIZE; ++i)
+ this->levels[i] = ci->levels[i];
+
+ for (unsigned i = 0; i < ci->GetAccessCount(); ++i)
+ {
+ ChanAccess *access = ci->GetAccess(i);
+ this->AddAccess(access->nc, access->level, access->creator, access->last_seen);
+ }
+ for (unsigned i = 0; i < ci->GetAkickCount(); ++i)
+ {
+ AutoKick *akick = ci->GetAkick(i);
+ if (akick->HasFlag(AK_ISNICK))
+ this->AddAkick(akick->creator, akick->nc, akick->reason, akick->addtime, akick->last_used);
+ else
+ this->AddAkick(akick->creator, akick->mask, akick->reason, akick->addtime, akick->last_used);
+ }
+ for (unsigned i = 0; i < ci->GetBadWordCount(); ++i)
+ {
+ BadWord *bw = ci->GetBadWord(i);
+ this->AddBadWord(bw->word, bw->type);
+ }
+}
+
/** Default destructor, cleans up the channel complete and removes it from the internal list
*/
ChannelInfo::~ChannelInfo()