summaryrefslogtreecommitdiff
path: root/modules/core
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2010-07-15 22:55:02 -0400
committerAdam <Adam@anope.org>2010-07-15 22:55:02 -0400
commita22f8d3b2de88b9bb6f80f0c2780846ae23ab389 (patch)
treea2fdf570868896a787df8a1169951b0028e68d6b /modules/core
parent43b1e43afb85639485e36d24da351dc0f121be6e (diff)
Moved some files and diretories around, made cmake skip files it knows it can't compile because of missing dependices.
Diffstat (limited to 'modules/core')
-rw-r--r--modules/core/bs_act.cpp85
-rw-r--r--modules/core/bs_assign.cpp95
-rw-r--r--modules/core/bs_badwords.cpp282
-rw-r--r--modules/core/bs_bot.cpp411
-rw-r--r--modules/core/bs_botlist.cpp93
-rw-r--r--modules/core/bs_help.cpp53
-rw-r--r--modules/core/bs_info.cpp233
-rw-r--r--modules/core/bs_kick.cpp388
-rw-r--r--modules/core/bs_say.cpp91
-rw-r--r--modules/core/bs_set.cpp215
-rw-r--r--modules/core/bs_unassign.cpp73
-rw-r--r--modules/core/cs_access.cpp623
-rw-r--r--modules/core/cs_akick.cpp584
-rw-r--r--modules/core/cs_ban.cpp111
-rw-r--r--modules/core/cs_clear.cpp178
-rw-r--r--modules/core/cs_drop.cpp116
-rw-r--r--modules/core/cs_forbid.cpp129
-rw-r--r--modules/core/cs_getkey.cpp75
-rw-r--r--modules/core/cs_help.cpp73
-rw-r--r--modules/core/cs_info.cpp158
-rw-r--r--modules/core/cs_invite.cpp94
-rw-r--r--modules/core/cs_kick.cpp97
-rw-r--r--modules/core/cs_list.cpp176
-rw-r--r--modules/core/cs_modes.cpp451
-rw-r--r--modules/core/cs_register.cpp131
-rw-r--r--modules/core/cs_saset.cpp122
-rw-r--r--modules/core/cs_saset_noexpire.cpp82
-rw-r--r--modules/core/cs_set.cpp127
-rw-r--r--modules/core/cs_set_bantype.cpp110
-rw-r--r--modules/core/cs_set_description.cpp104
-rw-r--r--modules/core/cs_set_entrymsg.cpp111
-rw-r--r--modules/core/cs_set_founder.cpp138
-rw-r--r--modules/core/cs_set_keeptopic.cpp109
-rw-r--r--modules/core/cs_set_mlock.cpp182
-rw-r--r--modules/core/cs_set_opnotice.cpp109
-rw-r--r--modules/core/cs_set_peace.cpp109
-rw-r--r--modules/core/cs_set_persist.cpp154
-rw-r--r--modules/core/cs_set_private.cpp109
-rw-r--r--modules/core/cs_set_restricted.cpp112
-rw-r--r--modules/core/cs_set_secure.cpp109
-rw-r--r--modules/core/cs_set_securefounder.cpp115
-rw-r--r--modules/core/cs_set_secureops.cpp109
-rw-r--r--modules/core/cs_set_signkick.cpp109
-rw-r--r--modules/core/cs_set_successor.cpp140
-rw-r--r--modules/core/cs_set_topiclock.cpp109
-rw-r--r--modules/core/cs_set_xop.cpp139
-rw-r--r--modules/core/cs_status.cpp74
-rw-r--r--modules/core/cs_suspend.cpp195
-rw-r--r--modules/core/cs_topic.cpp98
-rw-r--r--modules/core/cs_unban.cpp88
-rw-r--r--modules/core/cs_xop.cpp661
-rw-r--r--modules/core/db_plain.cpp1166
-rw-r--r--modules/core/enc_md5.cpp373
-rw-r--r--modules/core/enc_none.cpp67
-rw-r--r--modules/core/enc_old.cpp381
-rw-r--r--modules/core/enc_sha1.cpp229
-rw-r--r--modules/core/enc_sha256.cpp316
-rw-r--r--modules/core/hs_del.cpp73
-rw-r--r--modules/core/hs_delall.cpp78
-rw-r--r--modules/core/hs_group.cpp66
-rw-r--r--modules/core/hs_help.cpp51
-rw-r--r--modules/core/hs_list.cpp132
-rw-r--r--modules/core/hs_off.cpp62
-rw-r--r--modules/core/hs_on.cpp76
-rw-r--r--modules/core/hs_set.cpp165
-rw-r--r--modules/core/hs_setall.cpp164
-rw-r--r--modules/core/ms_cancel.cpp87
-rw-r--r--modules/core/ms_check.cpp106
-rw-r--r--modules/core/ms_del.cpp162
-rw-r--r--modules/core/ms_help.cpp52
-rw-r--r--modules/core/ms_info.cpp215
-rw-r--r--modules/core/ms_list.cpp176
-rw-r--r--modules/core/ms_read.cpp159
-rw-r--r--modules/core/ms_rsend.cpp90
-rw-r--r--modules/core/ms_send.cpp61
-rw-r--r--modules/core/ms_sendall.cpp77
-rw-r--r--modules/core/ms_set.cpp272
-rw-r--r--modules/core/ms_staff.cpp74
-rw-r--r--modules/core/ns_access.cpp181
-rw-r--r--modules/core/ns_alist.cpp155
-rw-r--r--modules/core/ns_drop.cpp130
-rw-r--r--modules/core/ns_forbid.cpp122
-rw-r--r--modules/core/ns_getemail.cpp83
-rw-r--r--modules/core/ns_getpass.cpp94
-rw-r--r--modules/core/ns_ghost.cpp103
-rw-r--r--modules/core/ns_group.cpp287
-rw-r--r--modules/core/ns_help.cpp67
-rw-r--r--modules/core/ns_identify.cpp125
-rw-r--r--modules/core/ns_info.cpp191
-rw-r--r--modules/core/ns_list.cpp219
-rw-r--r--modules/core/ns_logout.cpp96
-rw-r--r--modules/core/ns_recover.cpp124
-rw-r--r--modules/core/ns_register.cpp395
-rw-r--r--modules/core/ns_release.cpp106
-rw-r--r--modules/core/ns_resetpass.cpp169
-rw-r--r--modules/core/ns_saset.cpp245
-rw-r--r--modules/core/ns_saset_noexpire.cpp84
-rw-r--r--modules/core/ns_sendpass.cpp100
-rw-r--r--modules/core/ns_set.cpp229
-rw-r--r--modules/core/ns_set_autoop.cpp128
-rw-r--r--modules/core/ns_set_email.cpp157
-rw-r--r--modules/core/ns_set_greet.cpp127
-rw-r--r--modules/core/ns_set_hide.cpp197
-rw-r--r--modules/core/ns_set_kill.cpp180
-rw-r--r--modules/core/ns_set_language.cpp140
-rw-r--r--modules/core/ns_set_message.cpp146
-rw-r--r--modules/core/ns_set_private.cpp134
-rw-r--r--modules/core/ns_set_secure.cpp134
-rw-r--r--modules/core/ns_status.cpp77
-rw-r--r--modules/core/ns_suspend.cpp199
-rw-r--r--modules/core/ns_update.cpp67
-rw-r--r--modules/core/os_akill.cpp410
-rw-r--r--modules/core/os_chankill.cpp117
-rw-r--r--modules/core/os_chanlist.cpp99
-rw-r--r--modules/core/os_clearmodes.cpp189
-rw-r--r--modules/core/os_defcon.cpp499
-rw-r--r--modules/core/os_global.cpp62
-rw-r--r--modules/core/os_help.cpp51
-rw-r--r--modules/core/os_ignore.cpp203
-rw-r--r--modules/core/os_jupe.cpp78
-rw-r--r--modules/core/os_kick.cpp80
-rw-r--r--modules/core/os_mode.cpp71
-rw-r--r--modules/core/os_modinfo.cpp100
-rw-r--r--modules/core/os_modlist.cpp230
-rw-r--r--modules/core/os_modload.cpp73
-rw-r--r--modules/core/os_modunload.cpp75
-rw-r--r--modules/core/os_news.cpp476
-rw-r--r--modules/core/os_noop.cpp89
-rw-r--r--modules/core/os_oline.cpp83
-rw-r--r--modules/core/os_quit.cpp62
-rw-r--r--modules/core/os_reload.cpp64
-rw-r--r--modules/core/os_restart.cpp62
-rw-r--r--modules/core/os_session.cpp509
-rw-r--r--modules/core/os_set.cpp312
-rw-r--r--modules/core/os_shutdown.cpp61
-rw-r--r--modules/core/os_snline.cpp431
-rw-r--r--modules/core/os_sqline.cpp412
-rw-r--r--modules/core/os_staff.cpp82
-rw-r--r--modules/core/os_stats.cpp392
-rw-r--r--modules/core/os_svsnick.cpp99
-rw-r--r--modules/core/os_szline.cpp409
-rw-r--r--modules/core/os_umode.cpp86
-rw-r--r--modules/core/os_update.cpp54
-rw-r--r--modules/core/os_userlist.cpp100
-rw-r--r--modules/core/ss_main.cpp63
145 files changed, 24743 insertions, 0 deletions
diff --git a/modules/core/bs_act.cpp b/modules/core/bs_act.cpp
new file mode 100644
index 000000000..aa054d11b
--- /dev/null
+++ b/modules/core/bs_act.cpp
@@ -0,0 +1,85 @@
+/* BotServ 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 CommandBSAct : public Command
+{
+ public:
+ CommandBSAct() : Command("ACT", 2, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ ci::string message = params[1];
+
+ if (!check_access(u, ci, CA_SAY))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!ci->bi)
+ {
+ notice_help(Config.s_BotServ, u, BOT_NOT_ASSIGNED);
+ return MOD_CONT;
+ }
+
+ if (!ci->c || !ci->c->FindUser(ci->bi))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_NOT_ON_CHANNEL, ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ size_t i = 0;
+ while ((i = message.find_first_of("\001"), i) && i != std::string::npos)
+ message.erase(i, 1);
+
+ ircdproto->SendAction(ci->bi, ci->name.c_str(), "%s", message.c_str());
+ ci->bi->lastmsg = time(NULL);
+ if (Config.LogBot && Config.LogChannel && LogChan && !debug && findchan(Config.LogChannel))
+ ircdproto->SendPrivmsg(ci->bi, Config.LogChannel, "ACT %s %s %s", u->nick.c_str(), ci->name.c_str(), message.c_str());
+ return MOD_CONT;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "ACT", BOT_ACT_SYNTAX);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_ACT);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_ACT);
+ }
+};
+
+class BSAct : public Module
+{
+ public:
+ BSAct(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSAct());
+ }
+};
+
+MODULE_INIT(BSAct)
diff --git a/modules/core/bs_assign.cpp b/modules/core/bs_assign.cpp
new file mode 100644
index 000000000..0695a7e6d
--- /dev/null
+++ b/modules/core/bs_assign.cpp
@@ -0,0 +1,95 @@
+/* BotServ 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 CommandBSAssign : public Command
+{
+ public:
+ CommandBSAssign() : Command("ASSIGN", 2, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ const char *nick = params[1].c_str();
+ BotInfo *bi;
+ ChannelInfo *ci;
+
+ if (readonly)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_ASSIGN_READONLY);
+ return MOD_CONT;
+ }
+
+ if (!(bi = findbot(nick)))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_DOES_NOT_EXIST, nick);
+ return MOD_CONT;
+ }
+
+ ci = cs_findchan(chan);
+
+ if (ci->botflags.HasFlag(BS_NOBOT) || (!check_access(u, ci, CA_ASSIGN) && !u->Account()->HasPriv("botserv/administration")))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (bi->HasFlag(BI_PRIVATE) && !u->Account()->HasCommand("botserv/assign/private"))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (ci->bi && ci::string(ci->bi->nick.c_str()) == nick)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_ASSIGN_ALREADY, ci->bi->nick.c_str(), chan);
+ return MOD_CONT;
+ }
+
+ bi->Assign(u, ci);
+ notice_lang(Config.s_BotServ, u, BOT_ASSIGN_ASSIGNED, bi->nick.c_str(), ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_ASSIGN);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "ASSIGN", BOT_ASSIGN_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_ASSIGN);
+ }
+};
+
+class BSAssign : public Module
+{
+ public:
+ BSAssign(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSAssign);
+ }
+};
+
+MODULE_INIT(BSAssign)
diff --git a/modules/core/bs_badwords.cpp b/modules/core/bs_badwords.cpp
new file mode 100644
index 000000000..822847a6a
--- /dev/null
+++ b/modules/core/bs_badwords.cpp
@@ -0,0 +1,282 @@
+/* BotServ 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 BadwordsListCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ bool SentHeader;
+ public:
+ BadwordsListCallback(User *_u, ChannelInfo *_ci, const std::string &list) : NumberList(list, false), u(_u), ci(_ci), SentHeader(false)
+ {
+ }
+
+ ~BadwordsListCallback()
+ {
+ if (!SentHeader)
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_NO_MATCH, ci->name.c_str());
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetBadWordCount())
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_LIST_HEADER, ci->name.c_str());
+ }
+
+ DoList(u, ci, Number - 1, ci->GetBadWord(Number - 1));
+ }
+
+ static void DoList(User *u, ChannelInfo *ci, unsigned Number, BadWord *bw)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_LIST_FORMAT, Number + 1, bw->word.c_str(), ((bw->type == BW_SINGLE) ? "(SINGLE)" : ((bw->type == BW_START) ? "(START)" : ((bw->type == BW_END) ? "(END)" : ""))));
+ }
+};
+
+class BadwordsDelCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ unsigned Deleted;
+ public:
+ BadwordsDelCallback(User *_u, ChannelInfo *_ci, const std::string &list) : NumberList(list, true), u(_u), ci(_ci), Deleted(0)
+ {
+ }
+
+ ~BadwordsDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_NO_MATCH, ci->name.c_str());
+ else if (Deleted == 1)
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_DELETED_ONE, ci->name.c_str());
+ else
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_DELETED_SEVERAL, Deleted, ci->name.c_str());
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetBadWordCount())
+ return;
+
+ ++Deleted;
+ ci->EraseBadWord(Number - 1);
+ }
+};
+
+class CommandBSBadwords : public Command
+{
+ private:
+ CommandReturn DoList(User *u, ChannelInfo *ci, const ci::string &word)
+ {
+ if (!ci->GetBadWordCount())
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_LIST_EMPTY, ci->name.c_str());
+ else if (!word.empty() && strspn(word.c_str(), "1234567890,-") == word.length())
+ {
+ BadwordsListCallback list(u, ci, word.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i)
+ {
+ BadWord *bw = ci->GetBadWord(i);
+
+ if (!word.empty() && !Anope::Match(bw->word, word.c_str(), false))
+ continue;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_LIST_HEADER, ci->name.c_str());
+ }
+
+ BadwordsListCallback::DoList(u, ci, i, bw);
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_NO_MATCH, ci->name.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoAdd(User *u, ChannelInfo *ci, const ci::string &word)
+ {
+ size_t pos = word.find_last_of(" ");
+ BadWordType type = BW_ANY;
+ ci::string realword = word;
+
+ if (pos != ci::string::npos)
+ {
+ ci::string opt = ci::string(word, pos + 1);
+ if (!opt.empty())
+ {
+ if (opt == "SINGLE")
+ type = BW_SINGLE;
+ else if (opt == "START")
+ type = BW_START;
+ else if (opt == "END")
+ type = BW_END;
+ }
+ realword = ci::string(word, 0, pos);
+ }
+
+ if (ci->GetBadWordCount() >= Config.BSBadWordsMax)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_REACHED_LIMIT, Config.BSBadWordsMax);
+ return MOD_CONT;
+ }
+
+ for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i)
+ {
+ BadWord *bw = ci->GetBadWord(i);
+
+ if (!bw->word.empty() && ((Config.BSCaseSensitive && !stricmp(bw->word.c_str(), realword.c_str())) || (!Config.BSCaseSensitive && bw->word == realword.c_str())))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_ALREADY_EXISTS, bw->word.c_str(), ci->name.c_str());
+ return MOD_CONT;
+ }
+ }
+
+ ci->AddBadWord(realword.c_str(), type);
+
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_ADDED, realword.c_str(), ci->name.c_str());
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDelete(User *u, ChannelInfo *ci, const ci::string &word)
+ {
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (!word.empty() && isdigit(word[0]) && strspn(word.c_str(), "1234567890,-") == word.length())
+ {
+ BadwordsDelCallback list(u, ci, word.c_str());
+ list.Process();
+ }
+ else
+ {
+ unsigned i, end;
+ BadWord *badword;
+
+ for (i = 0, end = ci->GetBadWordCount(); i < end; ++i)
+ {
+ badword = ci->GetBadWord(i);
+
+ if (badword->word == word)
+ break;
+ }
+
+ if (i == end)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_NOT_FOUND, word.c_str(), ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ ci->EraseBadWord(i);
+
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_DELETED, badword->word.c_str(), ci->name.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoClear(User *u, ChannelInfo *ci)
+ {
+ ci->ClearBadWords();
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_CLEAR);
+ return MOD_CONT;
+ }
+ public:
+ CommandBSBadwords() : Command("BADWORDS", 2, 3)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ci::string cmd = params[1];
+ ci::string word = params.size() > 2 ? params[2].c_str() : "";
+ ChannelInfo *ci;
+ bool need_args = cmd == "LIST" || cmd == "CLEAR";
+
+ if (!need_args && word.empty())
+ {
+ this->OnSyntaxError(u, cmd);
+ return MOD_CONT;
+ }
+
+ ci = cs_findchan(chan);
+
+ if (!check_access(u, ci, CA_BADWORDS) && (!need_args || !u->Account()->HasPriv("botserv/administration")))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BADWORDS_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (cmd == "ADD")
+ return this->DoAdd(u, ci, word);
+ else if (cmd == "DEL")
+ return this->DoDelete(u, ci, word);
+ else if (cmd == "LIST")
+ return this->DoList(u, ci, word);
+ else if (cmd == "CLEAR")
+ return this->DoClear(u, ci);
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_BADWORDS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "BADWORDS", BOT_BADWORDS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_BADWORDS);
+ }
+};
+
+class BSBadwords : public Module
+{
+ public:
+ BSBadwords(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSBadwords);
+ }
+};
+
+MODULE_INIT(BSBadwords)
diff --git a/modules/core/bs_bot.cpp b/modules/core/bs_bot.cpp
new file mode 100644
index 000000000..3af9fa685
--- /dev/null
+++ b/modules/core/bs_bot.cpp
@@ -0,0 +1,411 @@
+/* BotServ 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 CommandBSBot : public Command
+{
+ private:
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[1].c_str();
+ const char *user = params[2].c_str();
+ const char *host = params[3].c_str();
+ const char *real = params[4].c_str();
+ const char *ch = NULL;
+ BotInfo *bi;
+
+ if (findbot(nick))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BOT_ALREADY_EXISTS, nick);
+ return MOD_CONT;
+ }
+
+ if (strlen(nick) > Config.NickLen)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ if (strlen(user) > Config.UserLen)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_LONG_IDENT, Config.UserLen);
+ return MOD_CONT;
+ }
+
+ if (strlen(user) > Config.HostLen)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_LONG_HOST, Config.HostLen);
+ return MOD_CONT;
+ }
+
+ /* Check the nick is valid re RFC 2812 */
+ if (isdigit(nick[0]) || nick[0] == '-')
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ for (ch = nick; *ch && ch - nick < Config.NickLen; ++ch)
+ if (!isvalidnick(*ch))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ /* check for hardcored ircd forbidden nicks */
+ if (!ircdproto->IsNickValid(nick))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ /* Check the host is valid re RFC 2812 */
+ if (!isValidHost(host, 3))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_HOST);
+ return MOD_CONT;
+ }
+
+ for (ch = user; *ch && ch - user < Config.UserLen; ++ch)
+ if (!isalnum(*ch))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_IDENT, Config.UserLen);
+ return MOD_CONT;
+ }
+
+ /* We check whether the nick is registered, and inform the user
+ * if so. You need to drop the nick manually before you can use
+ * it as a bot nick from now on -GD
+ */
+ if (findnick(nick))
+ {
+ notice_lang(Config.s_BotServ, u, NICK_ALREADY_REGISTERED, nick);
+ return MOD_CONT;
+ }
+
+ if (!(bi = new BotInfo(nick, user, host, real)))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BOT_CREATION_FAILED);
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_BotServ, u, BOT_BOT_ADDED, bi->nick.c_str(), bi->GetIdent().c_str(), bi->host, bi->realname);
+
+ FOREACH_MOD(I_OnBotCreate, OnBotCreate(bi));
+ return MOD_CONT;
+ }
+
+ CommandReturn DoChange(User *u, const std::vector<ci::string> &params)
+ {
+ const char *oldnick = params[1].c_str();
+ const char *nick = params.size() > 2 ? params[2].c_str() : NULL;
+ const char *user = params.size() > 3 ? params[3].c_str() : NULL;
+ const char *host = params.size() > 4 ? params[4].c_str() : NULL;
+ const char *real = params.size() > 5 ? params[5].c_str() : NULL;
+ const char *ch = NULL;
+ BotInfo *bi;
+
+ if (!oldnick || !nick)
+ {
+ this->OnSyntaxError(u, "CHANGE");
+ return MOD_CONT;
+ }
+
+ if (!(bi = findbot(oldnick)))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_DOES_NOT_EXIST, oldnick);
+ return MOD_CONT;
+ }
+
+ if (stricmp(oldnick, nick) && nickIsServices(oldnick, 0))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_DOES_NOT_EXIST, oldnick);
+ return MOD_CONT;
+ }
+
+ if (strlen(nick) > Config.NickLen)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ if (user && strlen(user) > Config.UserLen)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_LONG_IDENT, Config.UserLen);
+ return MOD_CONT;
+ }
+
+ if (host && strlen(host) > Config.HostLen)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_LONG_HOST, Config.HostLen);
+ return MOD_CONT;
+ }
+
+ if (stricmp(oldnick, nick) && nickIsServices(nick, 0))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_DOES_NOT_EXIST, oldnick);
+ return MOD_CONT;
+ }
+
+ /* Checks whether there *are* changes.
+ * Case sensitive because we may want to change just the case.
+ * And we must finally check that the nick is not already
+ * taken by another bot.
+ */
+ if (bi->nick == nick && (user ? bi->GetIdent() == user : 1) && (host ? !strcmp(bi->host, host) : 1) && (real ? !strcmp(bi->realname, real) : 1))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BOT_ANY_CHANGES);
+ return MOD_CONT;
+ }
+
+ /* Check the nick is valid re RFC 2812 */
+ if (isdigit(nick[0]) || nick[0] == '-')
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ for (ch = nick; *ch && ch - nick < Config.NickLen; ++ch)
+ if (!isvalidnick(*ch))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ /* check for hardcored ircd forbidden nicks */
+ if (!ircdproto->IsNickValid(nick))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_NICK);
+ return MOD_CONT;
+ }
+
+ if (host && !isValidHost(host, 3))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_HOST);
+ return MOD_CONT;
+ }
+
+ if (user)
+ for (ch = user; *ch && ch - user < Config.UserLen; ++ch)
+ if (!isalnum(*ch))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BAD_IDENT, Config.UserLen);
+ return MOD_CONT;
+ }
+
+ ci::string ci_bi_nick(bi->nick.c_str());
+ if (ci_bi_nick != nick && findbot(nick))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BOT_ALREADY_EXISTS, nick);
+ return MOD_CONT;
+ }
+
+ if (ci_bi_nick != nick)
+ {
+ /* We check whether the nick is registered, and inform the user
+ * if so. You need to drop the nick manually before you can use
+ * it as a bot nick from now on -GD
+ */
+ if (findnick(nick))
+ {
+ notice_lang(Config.s_BotServ, u, NICK_ALREADY_REGISTERED, nick);
+ return MOD_CONT;
+ }
+
+ /* The new nick is really different, so we remove the Q line for
+ the old nick. */
+ if (ircd->sqline)
+ {
+ XLine x(bi->nick.c_str());
+ ircdproto->SendSQLineDel(&x);
+ }
+
+ /* We check whether user with this nick is online, and kill it if so */
+ EnforceQlinedNick(nick, Config.s_BotServ);
+ }
+
+ if (user)
+ ircdproto->SendQuit(bi, "Quit: Be right back");
+ else
+ {
+ ircdproto->SendChangeBotNick(bi, nick);
+ XLine x(bi->nick.c_str(), "Reserved for services");
+ ircdproto->SendSQLine(&x);
+ }
+
+ if (bi->nick != nick)
+ bi->SetNewNick(nick);
+
+ if (user && bi->GetIdent() != user)
+ bi->SetIdent(user);
+ if (host && strcmp(bi->host, host))
+ bi->host = sstrdup(host);
+ if (real && strcmp(bi->realname, real))
+ bi->realname = sstrdup(real);
+
+ if (user)
+ {
+ ircdproto->SendClientIntroduction(bi->nick, bi->GetIdent(), bi->host, bi->realname, ircd->pseudoclient_mode, bi->GetUID());
+ XLine x(bi->nick.c_str(), "Reserved for services");
+ ircdproto->SendSQLine(&x);
+ bi->RejoinAll();
+ }
+
+ notice_lang(Config.s_BotServ, u, BOT_BOT_CHANGED, oldnick, bi->nick.c_str(), bi->GetIdent().c_str(), bi->host, bi->realname);
+
+ FOREACH_MOD(I_OnBotChange, OnBotChange(bi));
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[1].c_str();
+ BotInfo *bi;
+
+ if (!nick)
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (!(bi = findbot(nick)))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_DOES_NOT_EXIST, nick);
+ return MOD_CONT;
+ }
+
+ if (nickIsServices(nick, 0))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_DOES_NOT_EXIST, nick);
+ return MOD_CONT;
+ }
+
+ FOREACH_MOD(I_OnBotDelete, OnBotDelete(bi));
+
+ ircdproto->SendQuit(bi, "Quit: Help! I'm being deleted by %s!", u->nick.c_str());
+ XLine x(bi->nick.c_str());
+ ircdproto->SendSQLineDel(&x);
+
+ delete bi;
+ notice_lang(Config.s_BotServ, u, BOT_BOT_DELETED, nick);
+ return MOD_CONT;
+ }
+ public:
+ CommandBSBot() : Command("BOT", 1, 6)
+ {
+ this->SetFlag(CFLAG_STRIP_CHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (readonly)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BOT_READONLY);
+ return MOD_CONT;
+ }
+
+ if (cmd == "ADD")
+ {
+ // ADD nick user host real - 5
+ if (!u->Account()->HasCommand("botserv/bot/add"))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (params.size() < 5)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+
+ std::vector<ci::string> tempparams = params;
+ // ADD takes less params than CHANGE, so we need to take 6 if given and append it with a space to 5.
+ if (tempparams.size() >= 6)
+ tempparams[4] = tempparams[4] + " " + tempparams[5];
+
+ return this->DoAdd(u, tempparams);
+ }
+ else if (cmd == "CHANGE")
+ {
+ // CHANGE oldn newn user host real - 6
+ // but only oldn and newn are required
+ if (!u->Account()->HasCommand("botserv/bot/change"))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (params.size() < 3)
+ {
+ this->OnSyntaxError(u, "CHANGE");
+ return MOD_CONT;
+ }
+
+ return this->DoChange(u, params);
+ }
+ else if (cmd == "DEL")
+ {
+ // DEL nick
+ if (!u->Account()->HasCommand("botserv/bot/del"))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (params.size() < 1)
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ return this->DoDel(u, params);
+ }
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_SERVADMIN_HELP_BOT);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "BOT", BOT_BOT_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_BOT);
+ }
+};
+
+class BSBot : public Module
+{
+ public:
+ BSBot(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSBot());
+ }
+};
+
+MODULE_INIT(BSBot)
diff --git a/modules/core/bs_botlist.cpp b/modules/core/bs_botlist.cpp
new file mode 100644
index 000000000..44848b5cb
--- /dev/null
+++ b/modules/core/bs_botlist.cpp
@@ -0,0 +1,93 @@
+/* BotServ 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 CommandBSBotList : public Command
+{
+ public:
+ CommandBSBotList() : Command("BOTLIST", 0, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ unsigned count = 0;
+
+ if (BotListByNick.empty())
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BOTLIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ for (botinfo_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
+ {
+ BotInfo *bi = it->second;
+
+ if (!bi->HasFlag(BI_PRIVATE))
+ {
+ if (!count)
+ notice_lang(Config.s_BotServ, u, BOT_BOTLIST_HEADER);
+ ++count;
+ u->SendMessage(Config.s_BotServ, " %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host);
+ }
+ }
+
+ if (u->Account()->HasCommand("botserv/botlist") && count < BotListByNick.size())
+ {
+ notice_lang(Config.s_BotServ, u, BOT_BOTLIST_PRIVATE_HEADER);
+
+ for (botinfo_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
+ {
+ BotInfo *bi = it->second;
+
+ if (bi->HasFlag(BI_PRIVATE))
+ {
+ u->SendMessage(Config.s_BotServ, " %-15s (%s@%s)", bi->nick.c_str(), bi->GetIdent().c_str(), bi->host);
+ ++count;
+ }
+ }
+ }
+
+ if (!count)
+ notice_lang(Config.s_BotServ, u, BOT_BOTLIST_EMPTY);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_BOTLIST_FOOTER, count);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_BOTLIST);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_BOTLIST);
+ }
+};
+
+class BSBotList : public Module
+{
+ public:
+ BSBotList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSBotList());
+ }
+};
+
+MODULE_INIT(BSBotList)
diff --git a/modules/core/bs_help.cpp b/modules/core/bs_help.cpp
new file mode 100644
index 000000000..7183c6fe9
--- /dev/null
+++ b/modules/core/bs_help.cpp
@@ -0,0 +1,53 @@
+/* BotServ 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 CommandBSHelp : public Command
+{
+ public:
+ CommandBSHelp() : Command("HELP", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ this->SetFlag(CFLAG_STRIP_CHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ mod_help_cmd(findbot(Config.s_BotServ), u, params[0].c_str());
+ return MOD_CONT;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ // Abuse syntax error to display general list help.
+ notice_help(Config.s_BotServ, u, BOT_HELP);
+ for (CommandMap::const_iterator it = BotServ->Commands.begin(), it_end = BotServ->Commands.end(); it != it_end; ++it)
+ if (!Config.HidePrivilegedCommands || it->second->permission.empty() || (u->Account() && u->Account()->HasCommand(it->second->permission)))
+ it->second->OnServHelp(u);
+ notice_help(Config.s_BotServ, u, BOT_HELP_FOOTER, Config.BSMinUsers);
+ }
+};
+
+class BSHelp : public Module
+{
+ public:
+ BSHelp(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSHelp());
+ }
+};
+
+MODULE_INIT(BSHelp)
diff --git a/modules/core/bs_info.cpp b/modules/core/bs_info.cpp
new file mode 100644
index 000000000..7d8e6aea7
--- /dev/null
+++ b/modules/core/bs_info.cpp
@@ -0,0 +1,233 @@
+/* BotServ 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 CommandBSInfo : public Command
+{
+ private:
+ void send_bot_channels(User * u, BotInfo * bi)
+ {
+ char buf[307], *end;
+
+ *buf = 0;
+ end = buf;
+
+ for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it)
+ {
+ ChannelInfo *ci = it->second;
+
+ if (ci->bi == bi)
+ {
+ if (strlen(buf) + strlen(ci->name.c_str()) > 300)
+ {
+ u->SendMessage(Config.s_BotServ, "%s", buf);
+ *buf = 0;
+ end = buf;
+ }
+ end += snprintf(end, sizeof(buf) - (end - buf), " %s ", ci->name.c_str());
+ }
+ }
+
+ if (*buf)
+ u->SendMessage(Config.s_BotServ, "%s", buf);
+ return;
+ }
+ public:
+ CommandBSInfo() : Command("INFO", 1, 1)
+ {
+ this->SetFlag(CFLAG_STRIP_CHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ BotInfo *bi;
+ ChannelInfo *ci;
+ const char *query = params[0].c_str();
+
+ int need_comma = 0;
+ char buf[BUFSIZE], *end;
+ const char *commastr = getstring(u, COMMA_SPACE);
+
+ if ((bi = findbot(query)))
+ {
+ struct tm *tm;
+
+ notice_lang(Config.s_BotServ, u, BOT_INFO_BOT_HEADER, bi->nick.c_str());
+ notice_lang(Config.s_BotServ, u, BOT_INFO_BOT_MASK, bi->GetIdent().c_str(), bi->host);
+ notice_lang(Config.s_BotServ, u, BOT_INFO_BOT_REALNAME, bi->realname);
+ tm = localtime(&bi->created);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_BotServ, u, BOT_INFO_BOT_CREATED, buf);
+ notice_lang(Config.s_BotServ, u, BOT_INFO_BOT_OPTIONS, getstring(u, (bi->HasFlag(BI_PRIVATE) ? BOT_INFO_OPT_PRIVATE : BOT_INFO_OPT_NONE)));
+ notice_lang(Config.s_BotServ, u, BOT_INFO_BOT_USAGE, bi->chans.size());
+
+ if (u->Account()->HasPriv("botserv/administration"))
+ this->send_bot_channels(u, bi);
+ }
+ else if ((ci = cs_findchan(query)))
+ {
+ if (!check_access(u, ci, CA_FOUNDER) && !u->Account()->HasPriv("botserv/administration"))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_HEADER, ci->name.c_str());
+ if (ci->bi)
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_BOT, ci->bi->nick.c_str());
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_BOT_NONE);
+
+ if (ci->botflags.HasFlag(BS_KICK_BADWORDS))
+ {
+ if (ci->ttb[TTB_BADWORDS])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_BADWORDS_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_BADWORDS]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_BADWORDS, getstring(u, BOT_INFO_ACTIVE));
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_BADWORDS, getstring(u, BOT_INFO_INACTIVE));
+ if (ci->botflags.HasFlag(BS_KICK_BOLDS))
+ {
+ if (ci->ttb[TTB_BOLDS])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_BOLDS_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_BOLDS]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_BOLDS, getstring(u, BOT_INFO_ACTIVE));
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_BOLDS, getstring(u, BOT_INFO_INACTIVE));
+ if (ci->botflags.HasFlag(BS_KICK_CAPS))
+ {
+ if (ci->ttb[TTB_CAPS])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_CAPS_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_CAPS], ci->capsmin, ci->capspercent);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_CAPS_ON, getstring(u, BOT_INFO_ACTIVE), ci->capsmin, ci->capspercent);
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_CAPS_OFF, getstring(u, BOT_INFO_INACTIVE));
+ if (ci->botflags.HasFlag(BS_KICK_COLORS))
+ {
+ if (ci->ttb[TTB_COLORS])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_COLORS_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_COLORS]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_COLORS, getstring(u, BOT_INFO_ACTIVE));
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_COLORS, getstring(u, BOT_INFO_INACTIVE));
+ if (ci->botflags.HasFlag(BS_KICK_FLOOD))
+ {
+ if (ci->ttb[TTB_FLOOD])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_FLOOD_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_FLOOD], ci->floodlines, ci->floodsecs);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_FLOOD_ON, getstring(u, BOT_INFO_ACTIVE), ci->floodlines, ci->floodsecs);
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_FLOOD_OFF, getstring(u, BOT_INFO_INACTIVE));
+ if (ci->botflags.HasFlag(BS_KICK_REPEAT))
+ {
+ if (ci->ttb[TTB_REPEAT])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_REPEAT_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_REPEAT], ci->repeattimes);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_REPEAT_ON, getstring(u, BOT_INFO_ACTIVE), ci->repeattimes);
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_REPEAT_OFF, getstring(u, BOT_INFO_INACTIVE));
+ if (ci->botflags.HasFlag(BS_KICK_REVERSES))
+ {
+ if (ci->ttb[TTB_REVERSES])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_REVERSES_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_REVERSES]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_REVERSES, getstring(u, BOT_INFO_ACTIVE));
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_REVERSES, getstring(u, BOT_INFO_INACTIVE));
+ if (ci->botflags.HasFlag(BS_KICK_UNDERLINES))
+ {
+ if (ci->ttb[TTB_UNDERLINES])
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_UNDERLINES_BAN, getstring(u, BOT_INFO_ACTIVE), ci->ttb[TTB_UNDERLINES]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_UNDERLINES, getstring(u, BOT_INFO_ACTIVE));
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_KICK_UNDERLINES, getstring(u, BOT_INFO_INACTIVE));
+
+ end = buf;
+ *end = 0;
+ if (ci->botflags.HasFlag(BS_DONTKICKOPS))
+ {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s", getstring(u, BOT_INFO_OPT_DONTKICKOPS));
+ need_comma = 1;
+ }
+ if (ci->botflags.HasFlag(BS_DONTKICKVOICES))
+ {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? commastr : "", getstring(u, BOT_INFO_OPT_DONTKICKVOICES));
+ need_comma = 1;
+ }
+ if (ci->botflags.HasFlag(BS_FANTASY))
+ {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? commastr : "", getstring(u, BOT_INFO_OPT_FANTASY));
+ need_comma = 1;
+ }
+ if (ci->botflags.HasFlag(BS_GREET))
+ {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? commastr : "", getstring(u, BOT_INFO_OPT_GREET));
+ need_comma = 1;
+ }
+ if (ci->botflags.HasFlag(BS_NOBOT))
+ {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? commastr : "", getstring(u, BOT_INFO_OPT_NOBOT));
+ need_comma = 1;
+ }
+ if (ci->botflags.HasFlag(BS_SYMBIOSIS))
+ {
+ end += snprintf(end, sizeof(buf) - (end - buf), "%s%s", need_comma ? commastr : "", getstring(u, BOT_INFO_OPT_SYMBIOSIS));
+ need_comma = 1;
+ }
+ notice_lang(Config.s_BotServ, u, BOT_INFO_CHAN_OPTIONS, *buf ? buf : getstring(u, BOT_INFO_OPT_NONE));
+ }
+ else
+ notice_lang(Config.s_BotServ, u, BOT_INFO_NOT_FOUND, query);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_INFO);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "INFO", BOT_INFO_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_INFO);
+ }
+};
+
+class BSInfo : public Module
+{
+ public:
+ BSInfo(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSInfo());
+ }
+};
+
+MODULE_INIT(BSInfo)
diff --git a/modules/core/bs_kick.cpp b/modules/core/bs_kick.cpp
new file mode 100644
index 000000000..2f2784d93
--- /dev/null
+++ b/modules/core/bs_kick.cpp
@@ -0,0 +1,388 @@
+/* BotServ 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 CommandBSKick : public Command
+{
+ public:
+ CommandBSKick() : Command("KICK", 3, 6)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ci::string option = params[1];
+ ci::string value = params[2];
+ const char *ttb = params.size() > 3 ? params[3].c_str() : NULL;
+
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (readonly)
+ notice_lang(Config.s_BotServ, u, BOT_KICK_DISABLED);
+ else if (!chan || option.empty() || value.empty())
+ syntax_error(Config.s_BotServ, u, "KICK", BOT_KICK_SYNTAX);
+ else if (value != "ON" && value != "OFF")
+ syntax_error(Config.s_BotServ, u, "KICK", BOT_KICK_SYNTAX);
+ else if (!check_access(u, ci, CA_SET) && !u->Account()->HasPriv("botserv/administration"))
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ else if (!ci->bi)
+ notice_help(Config.s_BotServ, u, BOT_NOT_ASSIGNED);
+ else
+ {
+ if (option == "BADWORDS")
+ {
+ if (value == "ON")
+ {
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_BADWORDS] = strtol(ttb, NULL, 10);
+ /* Only error if errno returns ERANGE or EINVAL or we are less then 0 - TSL */
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_BADWORDS] < 0)
+ {
+ /* leaving the debug behind since we might want to know what these are */
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_BADWORDS];
+ /* reset the value back to 0 - TSL */
+ ci->ttb[TTB_BADWORDS] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_BADWORDS] = 0;
+ ci->botflags.SetFlag(BS_KICK_BADWORDS);
+ if (ci->ttb[TTB_BADWORDS])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BADWORDS_ON_BAN, ci->ttb[TTB_BADWORDS]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BADWORDS_ON);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_BADWORDS);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BADWORDS_OFF);
+ }
+ }
+ else if (option == "BOLDS")
+ {
+ if (value == "ON")
+ {
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_BOLDS] = strtol(ttb, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_BOLDS] < 0)
+ {
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_BOLDS];
+ ci->ttb[TTB_BOLDS] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_BOLDS] = 0;
+ ci->botflags.SetFlag(BS_KICK_BOLDS);
+ if (ci->ttb[TTB_BOLDS])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BOLDS_ON_BAN, ci->ttb[TTB_BOLDS]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BOLDS_ON);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_BOLDS);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BOLDS_OFF);
+ }
+ }
+ else if (option == "CAPS")
+ {
+ if (value == "ON")
+ {
+ const char *min = params.size() > 4 ? params[4].c_str() : NULL;
+ const char *percent = params.size() > 5 ? params[5].c_str() : NULL;
+
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_CAPS] = strtol(ttb, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_CAPS] < 0)
+ {
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_CAPS];
+ ci->ttb[TTB_CAPS] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_CAPS] = 0;
+
+ if (!min)
+ ci->capsmin = 10;
+ else
+ ci->capsmin = atol(min);
+ if (ci->capsmin < 1)
+ ci->capsmin = 10;
+
+ if (!percent)
+ ci->capspercent = 25;
+ else
+ ci->capspercent = atol(percent);
+ if (ci->capspercent < 1 || ci->capspercent > 100)
+ ci->capspercent = 25;
+
+ ci->botflags.SetFlag(BS_KICK_CAPS);
+ if (ci->ttb[TTB_CAPS])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_CAPS_ON_BAN, ci->capsmin, ci->capspercent, ci->ttb[TTB_CAPS]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_CAPS_ON, ci->capsmin, ci->capspercent);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_CAPS);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_CAPS_OFF);
+ }
+ }
+ else if (option == "COLORS")
+ {
+ if (value == "ON")
+ {
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_COLORS] = strtol(ttb, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_COLORS] < 0)
+ {
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_COLORS];
+ ci->ttb[TTB_COLORS] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_COLORS] = 0;
+ ci->botflags.SetFlag(BS_KICK_COLORS);
+ if (ci->ttb[TTB_COLORS])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_COLORS_ON_BAN, ci->ttb[TTB_COLORS]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_COLORS_ON);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_COLORS);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_COLORS_OFF);
+ }
+ }
+ else if (option == "FLOOD")
+ {
+ if (value == "ON")
+ {
+ const char *lines = params.size() > 4 ? params[4].c_str() : NULL;
+ const char *secs = params.size() > 5 ? params[5].c_str() : NULL;
+
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_FLOOD] = strtol(ttb, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_FLOOD] < 0)
+ {
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_FLOOD];
+ ci->ttb[TTB_FLOOD] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_FLOOD] = 0;
+
+ if (!lines)
+ ci->floodlines = 6;
+ else
+ ci->floodlines = atol(lines);
+ if (ci->floodlines < 2)
+ ci->floodlines = 6;
+
+ if (!secs)
+ ci->floodsecs = 10;
+ else
+ ci->floodsecs = atol(secs);
+ if (ci->floodsecs < 1 || ci->floodsecs > Config.BSKeepData)
+ ci->floodsecs = 10;
+
+ ci->botflags.SetFlag(BS_KICK_FLOOD);
+ if (ci->ttb[TTB_FLOOD])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_FLOOD_ON_BAN, ci->floodlines, ci->floodsecs, ci->ttb[TTB_FLOOD]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_FLOOD_ON, ci->floodlines, ci->floodsecs);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_FLOOD);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_FLOOD_OFF);
+ }
+ }
+ else if (option == "REPEAT")
+ {
+ if (value == "ON")
+ {
+ const char *times = params.size() > 4 ? params[4].c_str() : NULL;
+
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_REPEAT] = strtol(ttb, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_REPEAT] < 0)
+ {
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_REPEAT];
+ ci->ttb[TTB_REPEAT] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_REPEAT] = 0;
+
+ if (!times)
+ ci->repeattimes = 3;
+ else
+ ci->repeattimes = atol(times);
+ if (ci->repeattimes < 2)
+ ci->repeattimes = 3;
+
+ ci->botflags.SetFlag(BS_KICK_REPEAT);
+ if (ci->ttb[TTB_REPEAT])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_REPEAT_ON_BAN, ci->repeattimes, ci->ttb[TTB_REPEAT]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_REPEAT_ON, ci->repeattimes);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_REPEAT);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_REPEAT_OFF);
+ }
+ }
+ else if (option == "REVERSES")
+ {
+ if (value == "ON")
+ {
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_REVERSES] = strtol(ttb, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_REVERSES] < 0)
+ {
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_REVERSES];
+ ci->ttb[TTB_REVERSES] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_REVERSES] = 0;
+ ci->botflags.SetFlag(BS_KICK_REVERSES);
+ if (ci->ttb[TTB_REVERSES])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_REVERSES_ON_BAN, ci->ttb[TTB_REVERSES]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_REVERSES_ON);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_REVERSES);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_REVERSES_OFF);
+ }
+ }
+ else if (option == "UNDERLINES")
+ {
+ if (value == "ON")
+ {
+ if (ttb)
+ {
+ errno = 0;
+ ci->ttb[TTB_UNDERLINES] = strtol(ttb, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL || ci->ttb[TTB_UNDERLINES] < 0)
+ {
+ Alog(LOG_DEBUG) << "errno is " << errno << " ERANGE " << ERANGE << " EINVAL " << EINVAL << " ttb " << ci->ttb[TTB_UNDERLINES];
+ ci->ttb[TTB_UNDERLINES] = 0;
+ notice_lang(Config.s_BotServ, u, BOT_KICK_BAD_TTB, ttb);
+ return MOD_CONT;
+ }
+ }
+ else
+ ci->ttb[TTB_UNDERLINES] = 0;
+ ci->botflags.SetFlag(BS_KICK_UNDERLINES);
+ if (ci->ttb[TTB_UNDERLINES])
+ notice_lang(Config.s_BotServ, u, BOT_KICK_UNDERLINES_ON_BAN, ci->ttb[TTB_UNDERLINES]);
+ else
+ notice_lang(Config.s_BotServ, u, BOT_KICK_UNDERLINES_ON);
+ }
+ else
+ {
+ ci->botflags.UnsetFlag(BS_KICK_UNDERLINES);
+ notice_lang(Config.s_BotServ, u, BOT_KICK_UNDERLINES_OFF);
+ }
+ }
+ else
+ notice_help(Config.s_BotServ, u, BOT_KICK_UNKNOWN, option.c_str());
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK);
+ else if (subcommand == "BADWORDS")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_BADWORDS);
+ else if (subcommand == "BOLDS")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_BOLDS);
+ else if (subcommand == "CAPS")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_CAPS);
+ else if (subcommand == "COLORS")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_COLORS);
+ else if (subcommand == "FLOOD")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_FLOOD);
+ else if (subcommand == "REPEAT")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_REPEAT);
+ else if (subcommand == "REVERSES")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_REVERSES);
+ else if (subcommand == "UNDERLINES")
+ notice_help(Config.s_BotServ, u, BOT_HELP_KICK_UNDERLINES);
+ else
+ return false;
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "KICK", BOT_KICK_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_KICK);
+ }
+};
+
+class BSKick : public Module
+{
+ public:
+ BSKick(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSKick());
+ }
+};
+
+MODULE_INIT(BSKick)
diff --git a/modules/core/bs_say.cpp b/modules/core/bs_say.cpp
new file mode 100644
index 000000000..88b298544
--- /dev/null
+++ b/modules/core/bs_say.cpp
@@ -0,0 +1,91 @@
+/* BotServ 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 CommandBSSay : public Command
+{
+ public:
+ CommandBSSay() : Command("SAY", 2, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci;
+
+ const char *chan = params[0].c_str();
+ const char *text = params[1].c_str();
+
+ ci = cs_findchan(chan);
+
+ if (!check_access(u, ci, CA_SAY))
+ {
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!ci->bi)
+ {
+ notice_help(Config.s_BotServ, u, BOT_NOT_ASSIGNED);
+ return MOD_CONT;
+ }
+
+ if (!ci->c || !ci->c->FindUser(ci->bi))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_NOT_ON_CHANNEL, ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ if (text[0] == '\001')
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ ircdproto->SendPrivmsg(ci->bi, ci->name.c_str(), "%s", text);
+ ci->bi->lastmsg = time(NULL);
+ if (Config.LogBot && Config.LogChannel && LogChan && !debug && findchan(Config.LogChannel))
+ ircdproto->SendPrivmsg(ci->bi, Config.LogChannel, "SAY %s %s %s", u->nick.c_str(), ci->name.c_str(), text);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_SAY);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "SAY", BOT_SAY_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_SAY);
+ }
+};
+
+class BSSay : public Module
+{
+ public:
+ BSSay(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSSay());
+ }
+};
+
+MODULE_INIT(BSSay)
diff --git a/modules/core/bs_set.cpp b/modules/core/bs_set.cpp
new file mode 100644
index 000000000..d6f70352a
--- /dev/null
+++ b/modules/core/bs_set.cpp
@@ -0,0 +1,215 @@
+/* BotServ 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 CommandBSSet : public Command
+{
+ public:
+ CommandBSSet() : Command("SET", 3, 3)
+ {
+ this->SetFlag(CFLAG_STRIP_CHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ci::string option = params[1];
+ ci::string value = params[2];
+ ChannelInfo *ci;
+
+ if (readonly)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_SET_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (u->Account()->HasCommand("botserv/set/private") && option == "PRIVATE")
+ {
+ BotInfo *bi;
+
+ if (!(bi = findbot(chan)))
+ {
+ notice_lang(Config.s_BotServ, u, BOT_DOES_NOT_EXIST, chan);
+ return MOD_CONT;
+ }
+
+ if (value == "ON")
+ {
+ bi->SetFlag(BI_PRIVATE);
+ notice_lang(Config.s_BotServ, u, BOT_SET_PRIVATE_ON, bi->nick.c_str());
+ }
+ else if (value == "OFF")
+ {
+ bi->UnsetFlag(BI_PRIVATE);
+ notice_lang(Config.s_BotServ, u, BOT_SET_PRIVATE_OFF, bi->nick.c_str());
+ }
+ else
+ syntax_error(Config.s_BotServ, u, "SET PRIVATE", BOT_SET_PRIVATE_SYNTAX);
+ return MOD_CONT;
+ }
+ else if (!(ci = cs_findchan(chan)))
+ notice_lang(Config.s_BotServ, u, CHAN_X_NOT_REGISTERED, chan);
+ else if (!u->Account()->HasPriv("botserv/administration") && !check_access(u, ci, CA_SET))
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ else
+ {
+ if (option == "DONTKICKOPS")
+ {
+ if (value == "ON")
+ {
+ ci->botflags.SetFlag(BS_DONTKICKOPS);
+ notice_lang(Config.s_BotServ, u, BOT_SET_DONTKICKOPS_ON, ci->name.c_str());
+ }
+ else if (value == "OFF")
+ {
+ ci->botflags.UnsetFlag(BS_DONTKICKOPS);
+ notice_lang(Config.s_BotServ, u, BOT_SET_DONTKICKOPS_OFF, ci->name.c_str());
+ }
+ else
+ syntax_error(Config.s_BotServ, u, "SET DONTKICKOPS", BOT_SET_DONTKICKOPS_SYNTAX);
+ }
+ else if (option == "DONTKICKVOICES")
+ {
+ if (value == "ON")
+ {
+ ci->botflags.SetFlag(BS_DONTKICKVOICES);
+ notice_lang(Config.s_BotServ, u, BOT_SET_DONTKICKVOICES_ON, ci->name.c_str());
+ }
+ else if (value == "OFF")
+ {
+ ci->botflags.UnsetFlag(BS_DONTKICKVOICES);
+ notice_lang(Config.s_BotServ, u, BOT_SET_DONTKICKVOICES_OFF, ci->name.c_str());
+ }
+ else
+ syntax_error(Config.s_BotServ, u, "SET DONTKICKVOICES", BOT_SET_DONTKICKVOICES_SYNTAX);
+ }
+ else if (option == "FANTASY")
+ {
+ if (value == "ON")
+ {
+ ci->botflags.SetFlag(BS_FANTASY);
+ notice_lang(Config.s_BotServ, u, BOT_SET_FANTASY_ON, ci->name.c_str());
+ }
+ else if (value == "OFF")
+ {
+ ci->botflags.UnsetFlag(BS_FANTASY);
+ notice_lang(Config.s_BotServ, u, BOT_SET_FANTASY_OFF, ci->name.c_str());
+ }
+ else
+ syntax_error(Config.s_BotServ, u, "SET FANTASY", BOT_SET_FANTASY_SYNTAX);
+ }
+ else if (option == "GREET")
+ {
+ if (value == "ON")
+ {
+ ci->botflags.SetFlag(BS_GREET);
+ notice_lang(Config.s_BotServ, u, BOT_SET_GREET_ON, ci->name.c_str());
+ }
+ else if (value == "OFF")
+ {
+ ci->botflags.UnsetFlag(BS_GREET);
+ notice_lang(Config.s_BotServ, u, BOT_SET_GREET_OFF, ci->name.c_str());
+ }
+ else
+ syntax_error(Config.s_BotServ, u, "SET GREET", BOT_SET_GREET_SYNTAX);
+ }
+ else if (u->Account()->HasCommand("botserv/set/nobot") && option == "NOBOT")
+ {
+ if (value == "ON")
+ {
+ ci->botflags.SetFlag(BS_NOBOT);
+ if (ci->bi)
+ ci->bi->UnAssign(u, ci);
+ notice_lang(Config.s_BotServ, u, BOT_SET_NOBOT_ON, ci->name.c_str());
+ }
+ else if (value == "OFF")
+ {
+ ci->botflags.UnsetFlag(BS_NOBOT);
+ notice_lang(Config.s_BotServ, u, BOT_SET_NOBOT_OFF, ci->name.c_str());
+ }
+ else
+ syntax_error(Config.s_BotServ, u, "SET NOBOT", BOT_SET_NOBOT_SYNTAX);
+ }
+ else if (option == "SYMBIOSIS")
+ {
+ if (value == "ON")
+ {
+ ci->botflags.SetFlag(BS_SYMBIOSIS);
+ notice_lang(Config.s_BotServ, u, BOT_SET_SYMBIOSIS_ON, ci->name.c_str());
+ }
+ else if (value == "OFF")
+ {
+ ci->botflags.UnsetFlag(BS_SYMBIOSIS);
+ notice_lang(Config.s_BotServ, u, BOT_SET_SYMBIOSIS_OFF, ci->name.c_str());
+ }
+ else
+ syntax_error(Config.s_BotServ, u, "SET SYMBIOSIS", BOT_SET_SYMBIOSIS_SYNTAX);
+ }
+ else
+ notice_help(Config.s_BotServ, u, BOT_SET_UNKNOWN, option.c_str());
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_SET);
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_BotServ, u, BOT_SERVADMIN_HELP_SET);
+ }
+ else if (subcommand == "DONTKICKOPS")
+ notice_help(Config.s_BotServ, u, BOT_HELP_SET_DONTKICKOPS);
+ else if (subcommand == "DONTKICKVOICES")
+ notice_help(Config.s_BotServ, u, BOT_HELP_SET_DONTKICKVOICES);
+ else if (subcommand == "FANTASY")
+ notice_help(Config.s_BotServ, u, BOT_HELP_SET_FANTASY);
+ else if (subcommand == "GREET")
+ notice_help(Config.s_BotServ, u, BOT_HELP_SET_GREET);
+ else if (subcommand == "SYMBIOSIS")
+ notice_lang(Config.s_BotServ, u, BOT_HELP_SET_SYMBIOSIS, Config.s_ChanServ);
+ else if (subcommand == "NOBOT")
+ notice_lang(Config.s_BotServ, u, BOT_SERVADMIN_HELP_SET_NOBOT);
+ else if (subcommand == "PRIVATE")
+ notice_lang(Config.s_BotServ, u, BOT_SERVADMIN_HELP_SET_PRIVATE);
+ else
+ return false;
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "SET", BOT_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_SET);
+ }
+};
+
+class BSSet : public Module
+{
+ public:
+ BSSet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSSet());
+ }
+};
+
+MODULE_INIT(BSSet)
diff --git a/modules/core/bs_unassign.cpp b/modules/core/bs_unassign.cpp
new file mode 100644
index 000000000..cf6d8d7ab
--- /dev/null
+++ b/modules/core/bs_unassign.cpp
@@ -0,0 +1,73 @@
+/* BotServ 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 CommandBSUnassign : public Command
+{
+ public:
+ CommandBSUnassign() : Command("UNASSIGN", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ChannelInfo *ci = cs_findchan(chan);
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PERM);
+
+ if (readonly)
+ notice_lang(Config.s_BotServ, u, BOT_ASSIGN_READONLY);
+ else if (!u->Account()->HasPriv("botserv/administration") && !check_access(u, ci, CA_ASSIGN))
+ notice_lang(Config.s_BotServ, u, ACCESS_DENIED);
+ else if (!ci->bi)
+ notice_help(Config.s_BotServ, u, BOT_NOT_ASSIGNED);
+ else if (ci->HasFlag(CI_PERSIST) && !cm)
+ notice_help(Config.s_BotServ, u, BOT_UNASSIGN_PERSISTANT_CHAN);
+ else
+ {
+ ci->bi->UnAssign(u, ci);
+ notice_lang(Config.s_BotServ, u, BOT_UNASSIGN_UNASSIGNED, ci->name.c_str());
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_BotServ, u, BOT_HELP_UNASSIGN);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_BotServ, u, "UNASSIGN", BOT_UNASSIGN_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_BotServ, u, BOT_HELP_CMD_UNASSIGN);
+ }
+};
+
+class BSUnassign : public Module
+{
+ public:
+ BSUnassign(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(BotServ, new CommandBSUnassign);
+ }
+};
+
+MODULE_INIT(BSUnassign)
diff --git a/modules/core/cs_access.cpp b/modules/core/cs_access.cpp
new file mode 100644
index 000000000..514771d57
--- /dev/null
+++ b/modules/core/cs_access.cpp
@@ -0,0 +1,623 @@
+/* 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 AccessListCallback : public NumberList
+{
+ protected:
+ User *u;
+ ChannelInfo *ci;
+ bool SentHeader;
+ public:
+ AccessListCallback(User *_u, ChannelInfo *_ci, const std::string &numlist) : NumberList(numlist, false), u(_u), ci(_ci), SentHeader(false)
+ {
+ }
+
+ ~AccessListCallback()
+ {
+ if (SentHeader)
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_FOOTER, ci->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_NO_MATCH, ci->name.c_str());
+ }
+
+ virtual void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAccessCount())
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
+ }
+
+ DoList(u, ci, Number - 1, ci->GetAccess(Number - 1));
+ }
+
+ static void DoList(User *u, ChannelInfo *ci, unsigned Number, ChanAccess *access)
+ {
+ if (ci->HasFlag(CI_XOP))
+ {
+ const char *xop = get_xop_level(access->level);
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_XOP_FORMAT, Number + 1, xop, access->nc->display);
+ }
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_AXS_FORMAT, Number + 1, access->level, access->nc->display);
+ }
+};
+
+class AccessViewCallback : public AccessListCallback
+{
+ public:
+ AccessViewCallback(User *_u, ChannelInfo *_ci, const std::string &numlist) : AccessListCallback(_u, _ci, numlist)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAccessCount())
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
+ }
+
+ DoList(u, ci, Number - 1, ci->GetAccess(Number - 1));
+ }
+
+ static void DoList(User *u, ChannelInfo *ci, unsigned Number, ChanAccess *access)
+ {
+ char timebuf[64];
+ struct tm tm;
+
+ memset(&timebuf, 0, sizeof(timebuf));
+ if (ci->c && u->Account() && nc_on_chan(ci->c, u->Account()))
+ snprintf(timebuf, sizeof(timebuf), "Now");
+ else if (access->last_seen == 0)
+ snprintf(timebuf, sizeof(timebuf), "Never");
+ else
+ {
+ tm = *localtime(&access->last_seen);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, &tm);
+ }
+
+ if (ci->HasFlag(CI_XOP))
+ {
+ const char *xop = get_xop_level(access->level);
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_VIEW_XOP_FORMAT, Number + 1, xop, access->nc->display, access->creator.c_str(), timebuf);
+ }
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_VIEW_AXS_FORMAT, Number + 1, access->level, access->nc->display, access->creator.c_str(), timebuf);
+ }
+};
+
+class AccessDelCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ unsigned Deleted;
+ std::string Nicks;
+ bool Denied;
+ public:
+ AccessDelCallback(User *_u, ChannelInfo *_ci, const std::string &numlist) : NumberList(numlist, true), u(_u), ci(_ci), Deleted(0), Denied(false)
+ {
+ }
+
+ ~AccessDelCallback()
+ {
+ if (Denied && !Deleted)
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (!Deleted)
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_NO_MATCH, ci->name.c_str());
+ else
+ {
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << get_access(u, ci) << ") deleted access of user" << (Deleted == 1 ? " " : "s ") << Nicks << " on " << ci->name;
+
+ if (Deleted == 1)
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_DELETED_ONE, ci->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_DELETED_SEVERAL, Deleted, ci->name.c_str());
+ }
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAccessCount())
+ return;
+
+ ChanAccess *access = ci->GetAccess(Number - 1);
+
+ if (get_access(u, ci) <= access->level && !u->Account()->HasPriv("chanserv/access/modify"))
+ {
+ Denied = true;
+ return;
+ }
+
+ ++Deleted;
+ if (!Nicks.empty())
+ Nicks += ", " + std::string(access->nc->display);
+ else
+ Nicks = access->nc->display;
+
+ FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access->nc));
+
+ ci->EraseAccess(Number - 1);
+ }
+};
+
+class CommandCSAccess : public Command
+{
+ CommandReturn DoAdd(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ const ci::string nick = params[2];
+ int level = atoi(params[3].c_str());
+ int ulev = get_access(u, ci);
+
+ if (level >= ulev && !u->Account()->HasPriv("chanserv/access/modify"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!level)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LEVEL_NONZERO);
+ return MOD_CONT;
+ }
+ else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
+ return MOD_CONT;
+ }
+
+ NickAlias *na = findnick(nick.c_str());
+ if (!na)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_NICKS_ONLY);
+ return MOD_CONT;
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_FORBIDDEN, nick.c_str());
+ return MOD_CONT;
+ }
+
+ NickCore *nc = na->nc;
+ ChanAccess *access = ci->GetAccess(nc);
+ if (access)
+ {
+ /* Don't allow lowering from a level >= ulev */
+ if (access->level >= ulev && !u->Account()->HasPriv("chanserv/access/modify"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ if (access->level == level)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LEVEL_UNCHANGED, access->nc->display, ci->name.c_str(), level);
+ return MOD_CONT;
+ }
+ access->level = level;
+
+ FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, na->nc, level));
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << ulev << ") set access level " << access->level << " to " << na->nick << " (group " << nc->display << ") on channel " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LEVEL_CHANGED, nc->display, ci->name.c_str(), level);
+ return MOD_CONT;
+ }
+
+ if (ci->GetAccessCount() >= Config.CSAccessMax)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_REACHED_LIMIT, Config.CSAccessMax);
+ return MOD_CONT;
+ }
+
+ ci->AddAccess(nc, level, u->nick);
+
+ FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, nc, level));
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << ulev << ") set access level " << level << " to " << na->nick << " (group " << nc->display << ") on channel " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_ADDED, nc->display, ci->name.c_str(), level);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ const ci::string nick = params[2];
+
+ if (!ci->GetAccessCount())
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, ci->name.c_str());
+ else if (isdigit(*nick.c_str()) && strspn(nick.c_str(), "1234567890,-") == nick.length())
+ {
+ AccessDelCallback list(u, ci, nick.c_str());
+ list.Process();
+ }
+ else
+ {
+ NickAlias *na = findnick(nick);
+ if (!na)
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_REGISTERED, nick.c_str());
+ return MOD_CONT;
+ }
+
+ NickCore *nc = na->nc;
+
+ unsigned i, end;
+ ChanAccess *access = NULL;
+ for (i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ access = ci->GetAccess(i);
+
+ if (access->nc == nc)
+ break;
+ }
+
+ if (i == end)
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_NOT_FOUND, nick.c_str(), ci->name.c_str());
+ else if (get_access(u, ci) <= access->level && !u->Account()->HasPriv("chanserv/access/modify"))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_DELETED, access->nc->display, ci->name.c_str());
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << get_access(u, ci) << ") deleted access of " << na->nick << " (group " << access->nc->display << ") on " << ci->name;
+ FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, na->nc));
+
+ ci->EraseAccess(i);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ const ci::string nick = params.size() > 2 ? params[2] : "";
+
+ if (!ci->GetAccessCount())
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, ci->name.c_str());
+ else if (!nick.empty() && strspn(nick.c_str(), "1234567890,-") == nick.length())
+ {
+ AccessListCallback list(u, ci, nick.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ ChanAccess *access = ci->GetAccess(i);
+
+ if (!nick.empty() && access->nc && !Anope::Match(access->nc->display, nick))
+ continue;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
+ }
+
+ AccessListCallback::DoList(u, ci, i, access);
+ }
+
+ if (SentHeader)
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_FOOTER, ci->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_NO_MATCH, ci->name.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoView(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ const ci::string nick = params.size() > 2 ? params[2] : "";
+
+ if (!ci->GetAccessCount())
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, ci->name.c_str());
+ else if (!nick.empty() && strspn(nick.c_str(), "1234567890,-") == nick.length())
+ {
+ AccessViewCallback list(u, ci, nick.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ ChanAccess *access = ci->GetAccess(i);
+
+ if (!nick.empty() && access->nc && !Anope::Match(access->nc->display, nick))
+ continue;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name.c_str());
+ }
+
+ AccessViewCallback::DoList(u, ci, i, access);
+ }
+
+ if (SentHeader)
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_LIST_FOOTER, ci->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_NO_MATCH, ci->name.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoClear(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ if (!IsFounder(u, ci) && !u->Account()->HasPriv("chanserv/access/modify"))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else
+ {
+ ci->ClearAccess();
+
+ FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u));
+
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_CLEAR, ci->name.c_str());
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << get_access(u, ci) << " cleared access list on " << ci->name;
+ }
+
+ return MOD_CONT;
+ }
+
+ public:
+ CommandCSAccess() : Command("ACCESS", 2, 4)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ci::string cmd = params[1];
+ const char *nick = params.size() > 2 ? params[2].c_str() : NULL;
+ const char *s = params.size() > 3 ? params[3].c_str() : NULL;
+
+ ChannelInfo *ci = cs_findchan(chan);
+
+ bool is_list = cmd == "LIST" || cmd == "VIEW";
+
+ /* If LIST, we don't *require* any parameters, but we can take any.
+ * If DEL, we require a nick and no level.
+ * Else (ADD), we require a level (which implies a nick). */
+ if (is_list || cmd == "CLEAR" ? 0 : (cmd == "DEL" ? (!nick || s) : !s))
+ this->OnSyntaxError(u, cmd);
+ /* We still allow LIST in xOP mode, but not others */
+ else if ((ci->HasFlag(CI_XOP)) && !is_list)
+ {
+ if (ModeManager::FindChannelModeByName(CMODE_HALFOP))
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_XOP_HOP, Config.s_ChanServ);
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_XOP, Config.s_ChanServ);
+ }
+ else if ((is_list && !check_access(u, ci, CA_ACCESS_LIST) && !u->Account()->HasCommand("chanserv/access/list")) || (!is_list && !check_access(u, ci, CA_ACCESS_CHANGE) && !u->Account()->HasPriv("chanserv/access/modify")))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (readonly && (cmd == "ADD" || cmd == "DEL" || cmd == "CLEAR"))
+ notice_lang(Config.s_ChanServ, u, CHAN_ACCESS_DISABLED);
+ else if (cmd == "ADD")
+ this->DoAdd(u, ci, params);
+ else if (cmd == "DEL")
+ this->DoDel(u, ci, params);
+ else if (cmd == "LIST")
+ this->DoList(u, ci, params);
+ else if (cmd == "VIEW")
+ this->DoView(u, ci, params);
+ else if (cmd == "CLEAR")
+ this->DoClear(u, ci, params);
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_ACCESS);
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_ACCESS_LEVELS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
+ }
+};
+
+class CommandCSLevels : public Command
+{
+ CommandReturn DoSet(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ const ci::string what = params[2];
+ const ci::string lev = params[3];
+
+ char *error;
+ int level = strtol(lev.c_str(), &error, 10);
+
+ if (lev == "FONDER")
+ {
+ level = ACCESS_FOUNDER;
+ *error = '\0';
+ }
+
+ if (*error)
+ this->OnSyntaxError(u, "SET");
+ else if (level <= ACCESS_INVALID || level > ACCESS_FOUNDER)
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
+ else
+ for (int i = 0; levelinfo[i].what >= 0; ++i)
+ {
+ if (levelinfo[i].name == what)
+ {
+ ci->levels[levelinfo[i].what] = level;
+ FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, level));
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " set level " << levelinfo[i].name << " on channel " << ci->name << " to " << level;
+ if (level == ACCESS_FOUNDER)
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_CHANGED_FOUNDER, levelinfo[i].name, ci->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_CHANGED, levelinfo[i].name, ci->name.c_str(), level);
+ return MOD_CONT;
+ }
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what.c_str(), Config.s_ChanServ);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDisable(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ const ci::string what = params[2];
+
+ /* Don't allow disabling of the founder level. It would be hard to change it back if you dont have access to use this command */
+ if (what != "FOUNDER")
+ for (int i = 0; levelinfo[i].what >= 0; ++i)
+ {
+ if (levelinfo[i].name == what)
+ {
+ ci->levels[levelinfo[i].what] = ACCESS_INVALID;
+ FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, i, levelinfo[i].what));
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " disabled level " << levelinfo[i].name << " on channel " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_DISABLED, levelinfo[i].name, ci->name.c_str());
+ return MOD_CONT;
+ }
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what.c_str(), Config.s_ChanServ);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_LIST_HEADER, ci->name.c_str());
+
+ if (!levelinfo_maxwidth)
+ for (int i = 0; levelinfo[i].what >= 0; ++i)
+ {
+ int len = strlen(levelinfo[i].name);
+ if (len > levelinfo_maxwidth)
+ levelinfo_maxwidth = len;
+ }
+
+ for (int i = 0; levelinfo[i].what >= 0; ++i)
+ {
+ int j = ci->levels[levelinfo[i].what];
+
+ if (j == ACCESS_INVALID)
+ {
+ j = levelinfo[i].what;
+
+ if (j == CA_AUTOOP || j == CA_AUTODEOP || j == CA_AUTOVOICE || j == CA_NOJOIN)
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED, levelinfo_maxwidth, levelinfo[i].name);
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED, levelinfo_maxwidth, levelinfo[i].name);
+ }
+ else if (j == ACCESS_FOUNDER)
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_LIST_FOUNDER, levelinfo_maxwidth, levelinfo[i].name);
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_LIST_NORMAL, levelinfo_maxwidth, levelinfo[i].name, j);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoReset(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ reset_levels(ci);
+ FOREACH_MOD(I_OnLevelChange, OnLevelChange(u, ci, -1, 0));
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " reset levels definitions on channel " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_RESET, ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ public:
+ CommandCSLevels() : Command("LEVELS", 2, 4)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ci::string cmd = params[1];
+ const char *what = params.size() > 2 ? params[2].c_str() : NULL;
+ const char *s = params.size() > 3 ? params[3].c_str() : NULL;
+
+ ChannelInfo *ci = cs_findchan(chan);
+
+ /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only
+ * one; else, we want none.
+ */
+ if (cmd == "SET" ? !s : (cmd.substr(0, 3) == "DIS" ? (!what || s) : !!what))
+ this->OnSyntaxError(u, cmd);
+ else if (ci->HasFlag(CI_XOP))
+ notice_lang(Config.s_ChanServ, u, CHAN_LEVELS_XOP);
+ else if (!check_access(u, ci, CA_FOUNDER) && !u->Account()->HasPriv("chanserv/access/modify"))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (cmd == "SET")
+ this->DoSet(u, ci, params);
+ else if (cmd == "DIS" || cmd == "DISABLE")
+ this->DoDisable(u, ci, params);
+ else if (cmd == "LIST")
+ this->DoList(u, ci, params);
+ else if (cmd == "RESET")
+ this->DoReset(u, ci, params);
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_LEVELS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_ACCESS);
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_LEVELS);
+ }
+};
+
+class CSAccess : public Module
+{
+ public:
+ CSAccess(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSAccess());
+ this->AddCommand(ChanServ, new CommandCSLevels());
+ }
+};
+
+MODULE_INIT(CSAccess)
diff --git a/modules/core/cs_akick.cpp b/modules/core/cs_akick.cpp
new file mode 100644
index 000000000..771389207
--- /dev/null
+++ b/modules/core/cs_akick.cpp
@@ -0,0 +1,584 @@
+/* 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"
+
+/* Split a usermask up into its constitutent parts. Returned strings are
+ * malloc()'d, and should be free()'d when done with. Returns "*" for
+ * missing parts.
+ */
+
+static void split_usermask(const char *mask, const char **nick, const char **user, const char **host)
+{
+ char *mask2 = sstrdup(mask);
+
+ *nick = strtok(mask2, "!");
+ *user = strtok(NULL, "@");
+ *host = strtok(NULL, "");
+ /* Handle special case: mask == user@host */
+ if (*nick && !*user && strchr(*nick, '@'))
+ {
+ *nick = NULL;
+ *user = strtok(mask2, "@");
+ *host = strtok(NULL, "");
+ }
+ if (!*nick)
+ *nick = "*";
+ if (!*user)
+ *user = "*";
+ if (!*host)
+ *host = "*";
+ *nick = sstrdup(*nick);
+ *user = sstrdup(*user);
+ *host = sstrdup(*host);
+ delete [] mask2;
+}
+
+class AkickListCallback : public NumberList
+{
+ protected:
+ User *u;
+ ChannelInfo *ci;
+ bool SentHeader;
+ public:
+ AkickListCallback(User *_u, ChannelInfo *_ci, const std::string &numlist) : NumberList(numlist, false), u(_u), ci(_ci), SentHeader(false)
+ {
+ }
+
+ ~AkickListCallback()
+ {
+ if (!SentHeader)
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_NO_MATCH, ci->name.c_str());
+ }
+
+ virtual void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAkickCount())
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name.c_str());
+ }
+
+ DoList(u, ci, Number - 1, ci->GetAkick(Number - 1));
+ }
+
+ static void DoList(User *u, ChannelInfo *ci, unsigned index, AutoKick *akick)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_FORMAT, index + 1, akick->HasFlag(AK_ISNICK) ? akick->nc->display : akick->mask.c_str(), !akick->reason.empty() ? akick->reason.c_str() : getstring(u, NO_REASON));
+ }
+};
+
+class AkickViewCallback : public AkickListCallback
+{
+ public:
+ AkickViewCallback(User *_u, ChannelInfo *_ci, const std::string &numlist) : AkickListCallback(u, ci, numlist)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAkickCount())
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name.c_str());
+ }
+
+ DoList(u, ci, Number - 1, ci->GetAkick(Number - 1));
+ }
+
+ static void DoList(User *u, ChannelInfo *ci, unsigned index, AutoKick *akick)
+ {
+ char timebuf[64];
+ struct tm tm;
+
+ memset(&timebuf, 0, sizeof(timebuf));
+
+ if (akick->addtime)
+ {
+ tm = *localtime(&akick->addtime);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, &tm);
+ }
+ else
+ snprintf(timebuf, sizeof(timebuf), "%s", getstring(u, UNKNOWN));
+
+ notice_lang(Config.s_ChanServ, u, (akick->HasFlag(AK_STUCK) ? CHAN_AKICK_VIEW_FORMAT_STUCK : CHAN_AKICK_VIEW_FORMAT), index + 1, (akick->HasFlag(AK_ISNICK)) ? akick->nc->display : akick->mask.c_str(), !akick->creator.empty() ? akick->creator.c_str() : getstring(u, UNKNOWN), timebuf, !akick->reason.empty() ? akick->reason.c_str() : getstring(u, NO_REASON));
+
+ if (akick->last_used)
+ {
+ char last_used[64];
+ tm = *localtime(&akick->last_used);
+ strftime_lang(last_used, sizeof(last_used), u, STRFTIME_SHORT_DATE_FORMAT, &tm);
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LAST_USED, last_used);
+ }
+ }
+};
+
+class AkickDelCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ unsigned Deleted;
+ public:
+ AkickDelCallback(User *_u, ChannelInfo *_ci, const std::string &list) : NumberList(list, true), u(_u), ci(_ci), Deleted(0)
+ {
+ }
+
+ ~AkickDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_NO_MATCH, ci->name.c_str());
+ else if (Deleted == 1)
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_DELETED_ONE, ci->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_DELETED_SEVERAL, Deleted, ci->name.c_str());
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAkickCount())
+ return;
+
+ ++Deleted;
+ ci->EraseAkick(Number - 1);
+ }
+};
+
+class CommandCSAKick : public Command
+{
+ void DoAdd(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ ci::string mask = params[2];
+ ci::string reason = params.size() > 3 ? params[3] : "";
+ NickAlias *na = findnick(mask);
+ NickCore *nc = NULL;
+ AutoKick *akick;
+
+ if (!na)
+ {
+ const char *nick, *user, *host;
+
+ split_usermask(mask.c_str(), &nick, &user, &host);
+ mask = ci::string(nick) + "!" + user + "@" + host;
+ delete [] nick;
+ delete [] user;
+ delete [] host;
+ }
+ else
+ {
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_FORBIDDEN, mask.c_str());
+ return;
+ }
+
+ nc = na->nc;
+ }
+
+ /* Check excepts BEFORE we get this far */
+ if (ModeManager::FindChannelModeByName(CMODE_EXCEPT) && is_excepted_mask(ci, mask.c_str()))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_EXCEPTED, mask.c_str(), ci->name.c_str());
+ return;
+ }
+
+ /* Check whether target nick has equal/higher access
+ * or whether the mask matches a user with higher/equal access - Viper */
+ if (ci->HasFlag(CI_PEACE) && nc)
+ {
+ if (nc == ci->founder || get_access_level(ci, nc) >= get_access(u, ci))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return;
+ }
+ }
+ else if (ci->HasFlag(CI_PEACE))
+ {
+ /* Match against all currently online users with equal or
+ * higher access. - Viper */
+ for (user_map::const_iterator it = UserListByNick.begin(), it_end = UserListByNick.end(); it != it_end; ++it)
+ {
+ User *u2 = it->second;
+
+ if ((check_access(u2, ci, CA_FOUNDER) || get_access(u2, ci) >= get_access(u, ci)) && match_usermask(mask.c_str(), u2))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return;
+ }
+ }
+
+ /* Match against the lastusermask of all nickalias's with equal
+ * or higher access. - Viper */
+ for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it)
+ {
+ NickAlias *na2 = it->second;
+
+ if (na2->HasFlag(NS_FORBIDDEN))
+ continue;
+
+ if (na2->nc && (na2->nc == ci->founder || get_access_level(ci, na2->nc) >= get_access(u, ci)))
+ {
+ char buf[BUFSIZE];
+
+ snprintf(buf, BUFSIZE, "%s!%s", na2->nick, na2->last_usermask);
+ if (Anope::Match(buf, mask.c_str(), false))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return;
+ }
+ }
+ }
+ }
+
+ for (unsigned j = 0, end = ci->GetAkickCount(); j < end; ++j)
+ {
+ akick = ci->GetAkick(j);
+ if (akick->HasFlag(AK_ISNICK) ? akick->nc == nc : akick->mask == mask)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_ALREADY_EXISTS, akick->HasFlag(AK_ISNICK) ? akick->nc->display : akick->mask.c_str(), ci->name.c_str());
+ return;
+ }
+ }
+
+
+ if (ci->GetAkickCount() >= Config.CSAutokickMax)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_REACHED_LIMIT, Config.CSAutokickMax);
+ return;
+ }
+
+ if (nc)
+ akick = ci->AddAkick(u->nick, nc, !reason.empty() ? reason.c_str() : "");
+ else
+ akick = ci->AddAkick(u->nick, mask.c_str(), !reason.empty() ? reason.c_str() : "");
+
+ FOREACH_MOD(I_OnAkickAdd, OnAkickAdd(u, ci, akick));
+
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_ADDED, mask.c_str(), ci->name.c_str());
+
+ this->DoEnforce(u, ci, params);
+ }
+
+ void DoStick(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ NickAlias *na;
+ NickCore *nc;
+ ci::string mask = params[2];
+ unsigned i, end;
+ AutoKick *akick;
+
+ if (!ci->GetAkickCount())
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, ci->name.c_str());
+ return;
+ }
+
+ na = findnick(mask);
+ nc = na ? na->nc : NULL;
+
+ for (i = 0, end = ci->GetAkickCount(); i < end; ++i)
+ {
+ akick = ci->GetAkick(i);
+
+ if (akick->HasFlag(AK_ISNICK))
+ continue;
+ if (akick->mask == mask)
+ break;
+ }
+
+ if (i == end)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask.c_str(), ci->name.c_str());
+ return;
+ }
+
+ akick->SetFlag(AK_STUCK);
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_STUCK, akick->mask.c_str(), ci->name.c_str());
+
+ if (ci->c)
+ stick_mask(ci, akick);
+ }
+
+ void DoUnStick(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ NickAlias *na;
+ NickCore *nc;
+ AutoKick *akick;
+ unsigned i, end;
+ ci::string mask = params[2];
+
+ if (!ci->GetAkickCount())
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, ci->name.c_str());
+ return;
+ }
+
+ na = findnick(mask);
+ nc = na ? na->nc : NULL;
+
+ for (i = 0, end = ci->GetAkickCount(); i < end; ++i)
+ {
+ akick = ci->GetAkick(i);
+
+ if (akick->HasFlag(AK_ISNICK))
+ continue;
+ if (akick->mask == mask)
+ break;
+ }
+
+ if (i == end)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask.c_str(), ci->name.c_str());
+ return;
+ }
+
+ akick->UnsetFlag(AK_STUCK);
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_UNSTUCK, akick->mask.c_str(), ci->name.c_str());
+ }
+
+ void DoDel(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ ci::string mask = params[2];
+ AutoKick *akick;
+ unsigned i, end;
+
+ if (!ci->GetAkickCount())
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, ci->name.c_str());
+ return;
+ }
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (isdigit(*mask.c_str()) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ AkickDelCallback list(u, ci, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ NickAlias *na = findnick(mask);
+ NickCore *nc = (na ? na->nc : NULL);
+
+ for (i = 0, end = ci->GetAkickCount(); i < end; ++i)
+ {
+ akick = ci->GetAkick(i);
+
+ if ((akick->HasFlag(AK_ISNICK) && akick->nc == nc) || (!akick->HasFlag(AK_ISNICK) && akick->mask == mask))
+ break;
+ }
+
+ if (i == ci->GetAkickCount())
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_NOT_FOUND, mask.c_str(), ci->name.c_str());
+ return;
+ }
+
+ ci->EraseAkick(i);
+
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_DELETED, mask.c_str(), ci->name.c_str());
+ }
+ }
+
+ void DoList(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ ci::string mask = params.size() > 2 ? params[2] : "";
+
+ if (!ci->GetAkickCount())
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, ci->name.c_str());
+ return;
+ }
+
+ if (!mask.empty() && isdigit(*mask.c_str()) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ AkickListCallback list(u, ci, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i)
+ {
+ AutoKick *akick = ci->GetAkick(i);
+
+ if (!mask.empty())
+ {
+ if (!akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->mask.c_str(), mask.c_str(), false))
+ continue;
+ if (akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->nc->display, mask.c_str(), false))
+ continue;
+ }
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name.c_str());
+ }
+
+ AkickListCallback::DoList(u, ci, i, akick);
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_NO_MATCH, ci->name.c_str());
+ }
+ }
+
+ void DoView(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ ci::string mask = params.size() > 2 ? params[2] : "";
+
+ if (!ci->GetAkickCount())
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_EMPTY, ci->name.c_str());
+ return;
+ }
+
+ if (!mask.empty() && isdigit(*mask.c_str()) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ AkickViewCallback list(u, ci, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = ci->GetAkickCount(); i < end; ++i)
+ {
+ AutoKick *akick = ci->GetAkick(i);
+
+ if (!mask.empty())
+ {
+ if (!akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->mask.c_str(), mask.c_str(), false))
+ continue;
+ if (akick->HasFlag(AK_ISNICK) && !Anope::Match(akick->nc->display, mask.c_str(), false))
+ continue;
+ }
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_LIST_HEADER, ci->name.c_str());
+ }
+
+ AkickViewCallback::DoList(u, ci, i, akick);
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_NO_MATCH, ci->name.c_str());
+ }
+ }
+
+ void DoEnforce(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ Channel *c = ci->c;
+ int count = 0;
+
+ if (!c)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, ci->name.c_str());
+ return;
+ }
+
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
+ {
+ UserContainer *uc = *it++;
+
+ if (ci->CheckKick(uc->user))
+ ++count;
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_ENFORCE_DONE, ci->name.c_str(), count);
+ }
+
+ void DoClear(User *u, ChannelInfo *ci, const std::vector<ci::string> &params)
+ {
+ ci->ClearAkick();
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_CLEAR, ci->name.c_str());
+ }
+
+ public:
+ CommandCSAKick() : Command("AKICK", 2, 4)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string chan = params[0];
+ ci::string cmd = params[1];
+ ci::string mask = params.size() > 2 ? params[2] : "";
+
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (mask.empty() && (cmd == "ADD" || cmd == "STICK" || cmd == "UNSTICK" || cmd == "DEL"))
+ this->OnSyntaxError(u, cmd);
+ else if (!check_access(u, ci, CA_AKICK) && !u->Account()->HasPriv("chanserv/access/modify"))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (cmd != "LIST" && cmd != "VIEW" && cmd != "ENFORCE" && readonly)
+ notice_lang(Config.s_ChanServ, u, CHAN_AKICK_DISABLED);
+ else if (cmd == "ADD")
+ this->DoAdd(u, ci, params);
+ else if (cmd == "STICK")
+ this->DoStick(u, ci, params);
+ else if (cmd == "UNSTICK")
+ this->DoUnStick(u, ci, params);
+ else if (cmd == "DEL")
+ this->DoDel(u, ci, params);
+ else if (cmd == "LIST")
+ this->DoList(u, ci, params);
+ else if (cmd == "VIEW")
+ this->DoView(u, ci, params);
+ else if (cmd == "ENFORCE")
+ this->DoEnforce(u, ci, params);
+ else if (cmd == "CLEAR")
+ this->DoClear(u, ci, params);
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_AKICK);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "AKICK", CHAN_AKICK_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_AKICK);
+ }
+};
+
+class CSAKick : public Module
+{
+ public:
+ CSAKick(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSAKick());
+ }
+};
+
+MODULE_INIT(CSAKick)
diff --git a/modules/core/cs_ban.cpp b/modules/core/cs_ban.cpp
new file mode 100644
index 000000000..17fbc1946
--- /dev/null
+++ b/modules/core/cs_ban.cpp
@@ -0,0 +1,111 @@
+/* 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 CommandCSBan : public Command
+{
+ public:
+ CommandCSBan(const ci::string &cname) : Command(cname, 2, 3)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ const char *target = params[1].c_str();
+ const char *reason = NULL;
+
+ if (params.size() > 2)
+ reason = params[2].c_str();
+
+ Channel *c = findchan(chan);
+ ChannelInfo *ci;
+ User *u2;
+
+ int is_same;
+
+ if (!reason)
+ reason = "Requested";
+
+ is_same = !stricmp(target, u->nick.c_str());
+
+ if (c)
+ ci = c->ci;
+
+ if (!c)
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ else if (is_same ? !(u2 = u) : !(u2 = finduser(target)))
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_IN_USE, target);
+ else if (!is_same ? !check_access(u, ci, CA_BAN) : !check_access(u, ci, CA_BANME))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (!is_same && (ci->HasFlag(CI_PEACE)) && (get_access(u2, ci) >= get_access(u, ci)))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ /*
+ * Dont ban/kick the user on channels where he is excepted
+ * to prevent services <-> server wars.
+ */
+ else if (ModeManager::FindChannelModeByName(CMODE_EXCEPT) && is_excepted(ci, u2))
+ notice_lang(Config.s_ChanServ, u, CHAN_EXCEPTED, u2->nick.c_str(), ci->name.c_str());
+ else if (u2->IsProtected())
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else
+ {
+ char mask[BUFSIZE];
+
+ get_idealban(ci, u2, mask, sizeof(mask));
+ c->SetMode(NULL, CMODE_BAN, mask);
+
+ /* We still allow host banning while not allowing to kick */
+ if (!c->FindUser(u2))
+ return MOD_CONT;
+
+ if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !check_access(u, ci, CA_SIGNKICK)))
+ c->Kick(whosends(ci), u2, "%s (%s)", reason, u->nick.c_str());
+ else
+ c->Kick(whosends(ci), u2, "%s", reason);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_BAN);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "BAN", CHAN_BAN_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_BAN);
+ }
+};
+
+class CSBan : public Module
+{
+ public:
+ CSBan(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSBan("BAN"));
+ this->AddCommand(ChanServ, new CommandCSBan("KB"));
+ }
+};
+
+MODULE_INIT(CSBan)
diff --git a/modules/core/cs_clear.cpp b/modules/core/cs_clear.cpp
new file mode 100644
index 000000000..7cc2631a4
--- /dev/null
+++ b/modules/core/cs_clear.cpp
@@ -0,0 +1,178 @@
+/* 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 CommandCSClear : public Command
+{
+ public:
+ CommandCSClear() : Command("CLEAR", 2, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ci::string what = params[1];
+ Channel *c = findchan(chan);
+ ChannelInfo *ci = c ? c->ci : NULL;
+ std::string modebuf;
+
+ ChannelMode *owner = ModeManager::FindChannelModeByName(CMODE_OWNER);
+ ChannelMode *admin = ModeManager::FindChannelModeByName(CMODE_PROTECT);
+ ChannelMode *op = ModeManager::FindChannelModeByName(CMODE_OP);
+ ChannelMode *halfop = ModeManager::FindChannelModeByName(CMODE_HALFOP);
+ ChannelMode *voice = ModeManager::FindChannelModeByName(CMODE_VOICE);
+
+ if (!c)
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ else if (!u || !check_access(u, ci, CA_CLEAR))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (what == "bans")
+ {
+ c->ClearBans();
+
+ notice_lang(Config.s_ChanServ, u, CHAN_CLEARED_BANS, chan);
+ }
+ else if (ModeManager::FindChannelModeByName(CMODE_EXCEPT) && what == "excepts")
+ {
+ c->ClearExcepts();
+
+ notice_lang(Config.s_ChanServ, u, CHAN_CLEARED_EXCEPTS, chan);
+ }
+ else if (ModeManager::FindChannelModeByName(CMODE_INVITE) && what == "invites")
+ {
+ c->ClearInvites();
+
+ notice_lang(Config.s_ChanServ, u, CHAN_CLEARED_INVITES, chan);
+ }
+ else if (what == "modes")
+ {
+ c->ClearModes();
+ check_modes(c);
+
+ notice_lang(Config.s_ChanServ, u, CHAN_CLEARED_MODES, chan);
+ }
+ else if (what == "ops")
+ {
+ if (ircd->svsmode_ucmode)
+ {
+ if (owner)
+ {
+ modebuf = '-';
+ modebuf += owner->ModeChar;
+
+ ircdproto->SendSVSModeChan(c, modebuf.c_str(), NULL);
+ }
+ if (admin)
+ {
+ modebuf = '-';
+ modebuf += admin->ModeChar;
+
+ ircdproto->SendSVSModeChan(c, modebuf.c_str(), NULL);
+ }
+ if (op)
+ {
+ modebuf = "-";
+ modebuf += op->ModeChar;
+
+ ircdproto->SendSVSModeChan(c, modebuf.c_str(), NULL);
+ }
+ }
+ else
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ UserContainer *uc = *it;
+
+ if (uc->Status->HasFlag(CMODE_OWNER))
+ c->RemoveMode(NULL, owner, uc->user->nick);
+ if (uc->Status->HasFlag(CMODE_PROTECT))
+ c->RemoveMode(NULL, admin, uc->user->nick);
+ if (uc->Status->HasFlag(CMODE_OP))
+ c->RemoveMode(NULL, op, uc->user->nick);
+ }
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_CLEARED_OPS, chan);
+ }
+ else if ((halfop && what == "hops") || (voice && what == "voices"))
+ {
+ ChannelMode *cm = what == "hops" ? halfop : voice;
+
+ if (ircd->svsmode_ucmode)
+ {
+ modebuf = "-";
+ modebuf += cm->ModeChar;
+
+ ircdproto->SendSVSModeChan(c, modebuf.c_str(), NULL);
+ }
+ else
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ UserContainer *uc = *it;
+
+ if (uc->Status->HasFlag(cm->Name))
+ c->RemoveMode(NULL, cm, uc->user->nick);
+ }
+ }
+ }
+ else if (what == "users")
+ {
+ std::string buf = "CLEAR USERS command from " + u->nick;
+
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
+ {
+ UserContainer *uc = *it++;
+
+ c->Kick(NULL, uc->user, buf.c_str());
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_CLEARED_USERS, chan);
+ }
+ else
+ syntax_error(Config.s_ChanServ, u, "CLEAR", CHAN_CLEAR_SYNTAX);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_CLEAR);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "CLEAR", CHAN_CLEAR_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_CLEAR);
+ }
+};
+
+class CSClear : public Module
+{
+ public:
+ CSClear(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSClear());
+ }
+};
+
+MODULE_INIT(CSClear)
diff --git a/modules/core/cs_drop.cpp b/modules/core/cs_drop.cpp
new file mode 100644
index 000000000..98daaee4f
--- /dev/null
+++ b/modules/core/cs_drop.cpp
@@ -0,0 +1,116 @@
+/* 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 CommandCSDrop : public Command
+{
+ public:
+ CommandCSDrop() : Command("DROP", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_FORBIDDEN);
+ this->SetFlag(CFLAG_ALLOW_SUSPENDED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ChannelInfo *ci;
+
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_DROP_DISABLED); // XXX: READ_ONLY_MODE?
+ return MOD_CONT;
+ }
+
+ ci = cs_findchan(chan);
+
+ if (ci->HasFlag(CI_FORBIDDEN) && !u->Account()->HasCommand("chanserv/drop"))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ return MOD_CONT;
+ }
+
+ if (ci->HasFlag(CI_SUSPENDED) && !u->Account()->HasCommand("chanserv/drop"))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+ return MOD_CONT;
+ }
+
+ if ((ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER)) && !u->Account()->HasCommand("chanserv/drop"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ int level = get_access(u, ci);
+
+ if (ci->c && ModeManager::FindChannelModeByName(CMODE_REGISTERED))
+ ci->c->RemoveMode(NULL, CMODE_REGISTERED, "", false);
+
+ if (ircd->chansqline && (ci->HasFlag(CI_FORBIDDEN)))
+ {
+ XLine x(ci->name.c_str());
+ ircdproto->SendSQLineDel(&x);
+ }
+
+ Alog() << Config.s_ChanServ << ": Channel " << ci->name << " dropped by " << u->GetMask() << " (founder: " << (ci->founder ? ci->founder->display : "(none)") << ")";
+
+ delete ci;
+
+ /* We must make sure that the Services admin has not normally the right to
+ * drop the channel before issuing the wallops.
+ */
+ if (Config.WallDrop && (level < ACCESS_FOUNDER || (!IsFounder(u, ci) && ci->HasFlag(CI_SECUREFOUNDER))))
+ ircdproto->SendGlobops(ChanServ, "\2%s\2 used DROP on channel \2%s\2", u->nick.c_str(), chan);
+
+ notice_lang(Config.s_ChanServ, u, CHAN_DROPPED, chan);
+
+ FOREACH_MOD(I_OnChanDrop, OnChanDrop(chan));
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_ChanServ, u, CHAN_SERVADMIN_HELP_DROP);
+ else
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_DROP);
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "DROP", CHAN_DROP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_DROP);
+ }
+};
+
+class CSDrop : public Module
+{
+ public:
+ CSDrop(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSDrop());
+ }
+};
+
+MODULE_INIT(CSDrop)
diff --git a/modules/core/cs_forbid.cpp b/modules/core/cs_forbid.cpp
new file mode 100644
index 000000000..a4e62eef7
--- /dev/null
+++ b/modules/core/cs_forbid.cpp
@@ -0,0 +1,129 @@
+/* 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 CommandCSForbid : public Command
+{
+ public:
+ CommandCSForbid() : Command("FORBID", 1, 2, "chanserv/forbid")
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTEREDCHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci;
+ const char *chan = params[0].c_str();
+ const char *reason = params.size() > 1 ? params[1].c_str() : NULL;
+
+ Channel *c;
+
+ if (Config.ForceForbidReason && !reason)
+ {
+ syntax_error(Config.s_ChanServ, u, "FORBID", CHAN_FORBID_SYNTAX_REASON);
+ return MOD_CONT;
+ }
+
+ if (*chan != '#')
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_SYMBOL_REQUIRED);
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+
+ if ((ci = cs_findchan(chan)))
+ delete ci;
+
+ ci = new ChannelInfo(chan);
+ if (!ci)
+ {
+ Alog() << Config.s_ChanServ << ": Valid FORBID for " << ci->name << " by " << u->nick << " failed";
+ notice_lang(Config.s_ChanServ, u, CHAN_FORBID_FAILED, chan);
+ return MOD_CONT;
+ }
+
+ ci->SetFlag(CI_FORBIDDEN);
+ ci->forbidby = sstrdup(u->nick.c_str());
+ if (reason)
+ ci->forbidreason = sstrdup(reason);
+
+ if ((c = findchan(ci->name)))
+ {
+ /* Before banning everyone, it might be prudent to clear +e and +I lists..
+ * to prevent ppl from rejoining.. ~ Viper */
+ c->ClearExcepts();
+ c->ClearInvites();
+
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
+ {
+ UserContainer *uc = *it++;
+
+ if (is_oper(uc->user))
+ continue;
+
+ c->Kick(ChanServ, uc->user, "%s", reason ? reason : getstring(uc->user->Account(), CHAN_FORBID_REASON));
+ }
+ }
+
+ if (Config.WallForbid)
+ ircdproto->SendGlobops(ChanServ, "\2%s\2 used FORBID on channel \2%s\2", u->nick.c_str(), ci->name.c_str());
+
+ if (ircd->chansqline)
+ {
+ XLine x(chan, "Forbidden");
+ ircdproto->SendSQLine(&x);
+ }
+
+ Alog() << Config.s_ChanServ << ": " << u->nick << " set FORBID for channel " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_FORBID_SUCCEEDED, chan);
+
+ FOREACH_MOD(I_OnChanForbidden, OnChanForbidden(ci));
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_SERVADMIN_HELP_FORBID);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "FORBID", CHAN_FORBID_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_FORBID);
+ }
+};
+
+class CSForbid : public Module
+{
+ public:
+ CSForbid(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSForbid());
+ }
+};
+
+MODULE_INIT(CSForbid)
diff --git a/modules/core/cs_getkey.cpp b/modules/core/cs_getkey.cpp
new file mode 100644
index 000000000..dc2f74655
--- /dev/null
+++ b/modules/core/cs_getkey.cpp
@@ -0,0 +1,75 @@
+/* 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 CommandCSGetKey : public Command
+{
+ public:
+ CommandCSGetKey() : Command("GETKEY", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ChannelInfo *ci;
+ std::string key;
+
+ ci = cs_findchan(chan);
+
+ if (!check_access(u, ci, CA_GETKEY) && !u->Account()->HasCommand("chanserv/getkey"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!ci->c || !ci->c->GetParam(CMODE_KEY, key))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_GETKEY_NOKEY, chan);
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_GETKEY_KEY, chan, key.c_str());
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_GETKEY);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "GETKEY", CHAN_GETKEY_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_GETKEY);
+ }
+};
+
+class CSGetKey : public Module
+{
+ public:
+ CSGetKey(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSGetKey());
+ }
+};
+
+MODULE_INIT(CSGetKey)
diff --git a/modules/core/cs_help.cpp b/modules/core/cs_help.cpp
new file mode 100644
index 000000000..536a6e423
--- /dev/null
+++ b/modules/core/cs_help.cpp
@@ -0,0 +1,73 @@
+/* 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 CommandCSHelp : public Command
+{
+ public:
+ CommandCSHelp() : Command("HELP", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ this->SetFlag(CFLAG_STRIP_CHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (cmd == "LEVELS DESC")
+ {
+ int i;
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_LEVELS_DESC);
+ if (!levelinfo_maxwidth)
+ for (i = 0; levelinfo[i].what >= 0; ++i)
+ {
+ int len = strlen(levelinfo[i].name);
+ if (len > levelinfo_maxwidth)
+ levelinfo_maxwidth = len;
+ }
+ for (i = 0; levelinfo[i].what >= 0; ++i)
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_LEVELS_DESC_FORMAT, levelinfo_maxwidth, levelinfo[i].name, getstring(u, levelinfo[i].desc));
+ }
+ else
+ mod_help_cmd(ChanServ, u, cmd.c_str());
+
+ return MOD_CONT;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP);
+ for (CommandMap::const_iterator it = ChanServ->Commands.begin(); it != ChanServ->Commands.end(); ++it)
+ if (!Config.HidePrivilegedCommands || it->second->permission.empty() || (u->Account() && u->Account()->HasCommand(it->second->permission)))
+ it->second->OnServHelp(u);
+ if (Config.CSExpire >= 86400)
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_EXPIRES, Config.CSExpire / 86400);
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_ChanServ, u, CHAN_SERVADMIN_HELP);
+ }
+};
+
+class CSHelp : public Module
+{
+ public:
+ CSHelp(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSHelp());
+ }
+};
+
+MODULE_INIT(CSHelp)
diff --git a/modules/core/cs_info.cpp b/modules/core/cs_info.cpp
new file mode 100644
index 000000000..9b70735dd
--- /dev/null
+++ b/modules/core/cs_info.cpp
@@ -0,0 +1,158 @@
+/* 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 CommandCSInfo : public Command
+{
+ void CheckOptStr(std::string &buf, ChannelInfoFlag opt, const std::string &str, ChannelInfo *ci, NickCore *nc)
+ {
+ if (ci->HasFlag(opt))
+ {
+ const char *commastr = getstring(nc, COMMA_SPACE);
+ if (!buf.empty())
+ buf += commastr;
+
+ buf += str;
+ }
+ }
+
+ public:
+ CommandCSInfo() : Command("INFO", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ this->SetFlag(CFLAG_ALLOW_SUSPENDED);
+ this->SetFlag(CFLAG_ALLOW_FORBIDDEN);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ char buf[BUFSIZE];
+ struct tm *tm;
+ bool has_auspex = u->IsIdentified() && u->Account()->HasPriv("chanserv/auspex");
+ bool show_all = false;
+ time_t expt;
+
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (ci->HasFlag(CI_FORBIDDEN))
+ {
+ if (is_oper(u) && ci->forbidby)
+ notice_lang(Config.s_ChanServ, u, CHAN_X_FORBIDDEN_OPER, chan, ci->forbidby, ci->forbidreason ? ci->forbidreason : getstring(u, NO_REASON));
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
+
+ return MOD_CONT;
+ }
+
+ /* Should we show all fields? Only for sadmins and identified users */
+ if (has_auspex && check_access(u, ci, CA_INFO))
+ show_all = true;
+
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_HEADER, chan);
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_NO_FOUNDER, ci->founder->display);
+
+ if (show_all && ci->successor)
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_NO_SUCCESSOR, ci->successor->display);
+
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_DESCRIPTION, ci->desc);
+ tm = localtime(&ci->time_registered);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_TIME_REGGED, buf);
+ tm = localtime(&ci->last_used);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_LAST_USED, buf);
+
+ if (ci->last_topic && (show_all || (!ci->HasMLock(CMODE_SECRET, true) && (!ci->c || !ci->c->HasMode(CMODE_SECRET)))))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_LAST_TOPIC, ci->last_topic);
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_TOPIC_SET_BY, ci->last_topic_setter.c_str());
+ }
+
+ if (ci->entry_message && show_all)
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_ENTRYMSG, ci->entry_message);
+
+ if (show_all)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_BANTYPE, ci->bantype);
+ std::string optbuf;
+
+ CheckOptStr(optbuf, CI_KEEPTOPIC, getstring(u, CHAN_INFO_OPT_KEEPTOPIC), ci, u->Account());
+ CheckOptStr(optbuf, CI_OPNOTICE, getstring(u, CHAN_INFO_OPT_OPNOTICE), ci, u->Account());
+ CheckOptStr(optbuf, CI_PEACE, getstring(u, CHAN_INFO_OPT_PEACE), ci, u->Account());
+ CheckOptStr(optbuf, CI_PRIVATE, getstring(u, CHAN_INFO_OPT_PRIVATE), ci, u->Account());
+ CheckOptStr(optbuf, CI_RESTRICTED, getstring(u, CHAN_INFO_OPT_RESTRICTED), ci, u->Account());
+ CheckOptStr(optbuf, CI_SECURE, getstring(u, CHAN_INFO_OPT_SECURE), ci, u->Account());
+ CheckOptStr(optbuf, CI_SECUREFOUNDER, getstring(u, CHAN_INFO_OPT_SECUREFOUNDER), ci, u->Account());
+ CheckOptStr(optbuf, CI_SECUREOPS, getstring(u, CHAN_INFO_OPT_SECUREOPS), ci, u->Account());
+ if (ci->HasFlag(CI_SIGNKICK))
+ CheckOptStr(optbuf, CI_SIGNKICK, getstring(u, CHAN_INFO_OPT_SIGNKICK), ci, u->Account());
+ else
+ CheckOptStr(optbuf, CI_SIGNKICK_LEVEL, getstring(u, CHAN_INFO_OPT_SIGNKICK), ci, u->Account());
+ CheckOptStr(optbuf, CI_TOPICLOCK, getstring(u, CHAN_INFO_OPT_TOPICLOCK), ci, u->Account());
+ CheckOptStr(optbuf, CI_XOP, getstring(u, CHAN_INFO_OPT_XOP), ci, u->Account());
+ CheckOptStr(optbuf, CI_PERSIST, getstring(u, CHAN_INFO_OPT_PERSIST), ci, u->Account());
+
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_OPTIONS, optbuf.empty() ? getstring(u, CHAN_INFO_OPT_NONE) : optbuf.c_str());
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_MODE_LOCK, get_mlock_modes(ci, 1));
+
+ // XXX: we could just as easily (and tidily) merge this in with the flags display above.
+ if (ci->HasFlag(CI_NO_EXPIRE))
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_NO_EXPIRE);
+ else
+ {
+ expt = ci->last_used + Config.CSExpire;
+ tm = localtime(&expt);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_ChanServ, u, CHAN_INFO_EXPIRE, buf);
+ }
+ }
+ if (ci->HasFlag(CI_SUSPENDED))
+ notice_lang(Config.s_ChanServ, u, CHAN_X_SUSPENDED, ci->forbidby, (ci->forbidreason ? ci->forbidreason : getstring(u, NO_REASON)));
+
+ FOREACH_MOD(I_OnChanInfo, OnChanInfo(u, ci, show_all));
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_INFO);
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "INFO", CHAN_INFO_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_INFO);
+ }
+};
+
+class CSInfo : public Module
+{
+ public:
+ CSInfo(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSInfo());
+ }
+};
+
+MODULE_INIT(CSInfo)
diff --git a/modules/core/cs_invite.cpp b/modules/core/cs_invite.cpp
new file mode 100644
index 000000000..48425e387
--- /dev/null
+++ b/modules/core/cs_invite.cpp
@@ -0,0 +1,94 @@
+/* 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 CommandCSInvite : public Command
+{
+ public:
+ CommandCSInvite() : Command("INVITE", 1, 3)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ Channel *c;
+ ChannelInfo *ci;
+ User *u2;
+
+ if (!(c = findchan(chan)))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ return MOD_CONT;
+ }
+
+ ci = c->ci;
+
+ if (!u || !check_access(u, ci, CA_INVITE))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (params.size() == 1)
+ u2 = u;
+ else
+ {
+ if (!(u2 = finduser(params[1])))
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_IN_USE, params[1].c_str());
+ return MOD_CONT;
+ }
+ }
+
+ if (c->FindUser(u2))
+ notice_lang(Config.s_ChanServ, u, CHAN_INVITE_ALREADY_IN, c->name.c_str());
+ else
+ {
+ ircdproto->SendInvite(whosends(ci), chan, u2->nick.c_str());
+ notice_lang(whosends(ci)->nick, u, CHAN_INVITE_OTHER_SUCCESS, u2->nick.c_str(), c->name.c_str());
+ notice_lang(whosends(ci)->nick, u2, CHAN_INVITE_SUCCESS, c->name.c_str());
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_INVITE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "INVITE", CHAN_INVITE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_INVITE);
+ }
+};
+
+class CSInvite : public Module
+{
+ public:
+ CSInvite(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSInvite());
+ }
+};
+
+MODULE_INIT(CSInvite)
diff --git a/modules/core/cs_kick.cpp b/modules/core/cs_kick.cpp
new file mode 100644
index 000000000..9aa4d30f6
--- /dev/null
+++ b/modules/core/cs_kick.cpp
@@ -0,0 +1,97 @@
+/* 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 CommandCSKick : public Command
+{
+ public:
+ CommandCSKick(const ci::string &cname) : Command(cname, 2, 3)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ const char *target = params[1].c_str();
+ const char *reason = NULL;
+
+ if (params.size() > 2)
+ reason = params[2].c_str();
+
+ Channel *c = findchan(chan);
+ ChannelInfo *ci;
+ User *u2;
+
+ int is_same;
+
+ if (!reason)
+ reason = "Requested";
+
+ is_same = target == u->nick.c_str() ? 1 : !stricmp(target, u->nick.c_str());
+
+ if (c)
+ ci = c->ci;
+
+ if (!c)
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ else if (is_same ? !(u2 = u) : !(u2 = finduser(target)))
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_IN_USE, target);
+ else if (!is_same ? !check_access(u, ci, CA_KICK) : !check_access(u, ci, CA_KICKME))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (!is_same && (ci->HasFlag(CI_PEACE)) && get_access(u2, ci) >= get_access(u, ci))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (u2->IsProtected())
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (!c->FindUser(u2))
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_ON_CHAN, u2->nick.c_str(), c->name.c_str());
+ else
+ {
+ if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !check_access(u, ci, CA_SIGNKICK)))
+ ci->c->Kick(whosends(ci), u2, "%s (%s)", reason, u->nick.c_str());
+ else
+ ci->c->Kick(whosends(ci), u2, "%s", reason);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_KICK);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "KICK", CHAN_KICK_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_KICK);
+ }
+};
+
+class CSKick : public Module
+{
+ public:
+ CSKick(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSKick("KICK"));
+ this->AddCommand(ChanServ, new CommandCSKick("K"));
+ }
+};
+
+MODULE_INIT(CSKick)
diff --git a/modules/core/cs_list.cpp b/modules/core/cs_list.cpp
new file mode 100644
index 000000000..5d7547499
--- /dev/null
+++ b/modules/core/cs_list.cpp
@@ -0,0 +1,176 @@
+/* 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"
+#include "hashcomp.h"
+
+class CommandCSList : public Command
+{
+public:
+ CommandCSList() : Command("LIST", 1, 2)
+ {
+ this->SetFlag(CFLAG_STRIP_CHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *pattern = params[0].c_str();
+ int spattern_size;
+ char *spattern;
+ unsigned nchans;
+ char buf[BUFSIZE];
+ bool is_servadmin = u->Account()->HasCommand("chanserv/list");
+ int count = 0, from = 0, to = 0, tofree = 0;
+ char *tmp = NULL;
+ char *s = NULL;
+ bool forbidden = false, suspended = false, channoexpire = false;
+
+ if (Config.CSListOpersOnly && !is_oper(u))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_STOP;
+ }
+
+ if (pattern[0] == '#')
+ {
+ tmp = myStrGetOnlyToken((pattern + 1), '-', 0); /* Read FROM out */
+ if (!tmp)
+ {
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ notice_lang(Config.s_ChanServ, u, CS_LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; ++s)
+ if (!isdigit(*s))
+ {
+ delete [] tmp;
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ notice_lang(Config.s_ChanServ, u, CS_LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ from = atoi(tmp);
+ delete [] tmp;
+ tmp = myStrGetTokenRemainder(pattern, '-', 1); /* Read TO out */
+ if (!tmp)
+ {
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ notice_lang(Config.s_ChanServ, u, CS_LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; ++s)
+ if (!isdigit(*s))
+ {
+ delete [] tmp;
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ notice_lang(Config.s_ChanServ, u, CS_LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ to = atoi(tmp);
+ delete [] tmp;
+ pattern = sstrdup("*");
+ tofree = 1;
+ }
+
+ nchans = 0;
+
+ if (is_servadmin && params.size() > 1)
+ {
+ ci::string keyword;
+ spacesepstream keywords(params[1].c_str());
+ while (keywords.GetToken(keyword))
+ {
+ if (keyword == "FORBIDDEN")
+ forbidden = true;
+ if (keyword == "SUSPENDED")
+ suspended = true;
+ if (keyword == "NOEXPIRE")
+ channoexpire = true;
+ }
+ }
+
+ spattern_size = (strlen(pattern) + 2) * sizeof(char);
+ spattern = new char[spattern_size];
+ snprintf(spattern, spattern_size, "#%s", pattern);
+
+ notice_lang(Config.s_ChanServ, u, CHAN_LIST_HEADER, pattern);
+
+ for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it)
+ {
+ ChannelInfo *ci = it->second;
+
+ if (!is_servadmin && (ci->HasFlag(CI_PRIVATE) || ci->HasFlag(CI_FORBIDDEN) || ci->HasFlag(CI_SUSPENDED)))
+ continue;
+ if (forbidden && !ci->HasFlag(CI_FORBIDDEN))
+ continue;
+ else if (suspended && !ci->HasFlag(CI_SUSPENDED))
+ continue;
+ else if (channoexpire && !ci->HasFlag(CI_NO_EXPIRE))
+ continue;
+
+ if (!stricmp(pattern, ci->name.c_str()) || !stricmp(spattern, ci->name.c_str()) || Anope::Match(ci->name, pattern, false) || Anope::Match(ci->name, spattern, false))
+ {
+ if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= Config.CSListMax)
+ {
+ char noexpire_char = ' ';
+ if (is_servadmin && (ci->HasFlag(CI_NO_EXPIRE)))
+ noexpire_char = '!';
+
+ if (ci->HasFlag(CI_FORBIDDEN))
+ snprintf(buf, sizeof(buf), "%-20s [Forbidden]", ci->name.c_str());
+ else if (ci->HasFlag(CI_SUSPENDED))
+ snprintf(buf, sizeof(buf), "%-20s [Suspended]", ci->name.c_str());
+ else
+ snprintf(buf, sizeof(buf), "%-20s %s", ci->name.c_str(), ci->desc ? ci->desc : "");
+
+ u->SendMessage(Config.s_ChanServ, " %c%s", noexpire_char, buf);
+ }
+ ++count;
+ }
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_LIST_END, nchans > Config.CSListMax ? Config.CSListMax : nchans, nchans);
+ delete [] spattern;
+ if (tofree)
+ delete [] pattern;
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_LIST);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "LIST", CHAN_LIST_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_LIST);
+ }
+};
+
+class CSList : public Module
+{
+public:
+ CSList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(ChanServ, new CommandCSList());
+ }
+};
+
+MODULE_INIT(CSList)
diff --git a/modules/core/cs_modes.cpp b/modules/core/cs_modes.cpp
new file mode 100644
index 000000000..dce79f50d
--- /dev/null
+++ b/modules/core/cs_modes.cpp
@@ -0,0 +1,451 @@
+/* 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"
+
+/** do_util: not a command, but does the job of others
+ * @param u The user doing the command
+ * @param cm A channel mode class
+ * @param chan The channel its being set on
+ * @param nick The nick the modes being set on
+ * @param set Is the mode being set or removed
+ * @param level The acecss level required to set this mode on someone else
+ * @param levelself The access level required to set this mode on yourself
+ * @param name The name, eg "OP" or "HALFOP"
+ * @param notice Flag required on a channel to send a notice
+ */
+static CommandReturn do_util(User *u, ChannelMode *cm, const char *chan, const char *nick, bool set, int level, int levelself, const std::string &name, ChannelInfoFlag notice)
+{
+ Channel *c = findchan(chan);
+ ChannelInfo *ci;
+ User *u2;
+
+ int is_same;
+
+ if (!nick)
+ nick = u->nick.c_str();
+
+ is_same = nick == u->nick ? 1 : !stricmp(nick, u->nick.c_str());
+
+ if (c)
+ ci = c->ci;
+
+ if (!c)
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ else if (is_same ? !(u2 = u) : !(u2 = finduser(nick)))
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_IN_USE, nick);
+ else if (is_same ? !check_access(u, ci, levelself) : !check_access(u, ci, level))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (!set && !is_same && (ci->HasFlag(CI_PEACE)) && (get_access(u2, ci) >= get_access(u, ci)))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (!set && u2->IsProtected() && !is_same)
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else if (!c->FindUser(u2))
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_ON_CHAN, u2->nick.c_str(), c->name.c_str());
+ else
+ {
+ if (set)
+ c->SetMode(NULL, cm, u2->nick);
+ else
+ c->RemoveMode(NULL, cm, u2->nick);
+
+ if (notice && ci->HasFlag(notice))
+ ircdproto->SendMessage(whosends(ci), c->name.c_str(), "%s command used for %s by %s", name.c_str(), u2->nick.c_str(), u->nick.c_str());
+ }
+
+ return MOD_CONT;
+}
+
+class CommandCSOp : public Command
+{
+ public:
+ CommandCSOp() : Command("OP", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OP);
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, true, CA_OPDEOP, CA_OPDEOPME, "OP", CI_OPNOTICE);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_OP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "OP", CHAN_OP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_OP);
+ }
+};
+
+class CommandCSDeOp : public Command
+{
+ public:
+ CommandCSDeOp() : Command("DEOP", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OP);
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, false, CA_OPDEOP, CA_OPDEOPME, "DEOP", CI_OPNOTICE);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_DEOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "DEOP", CHAN_DEOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_DEOP);
+ }
+};
+
+class CommandCSVoice : public Command
+{
+ public:
+ CommandCSVoice() : Command("VOICE", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_VOICE);
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, true, CA_VOICE, CA_VOICEME, "VOICE", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_VOICE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "VOICE", CHAN_VOICE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_VOICE);
+ }
+};
+
+class CommandCSDeVoice : public Command
+{
+ public:
+ CommandCSDeVoice() : Command("DEVOICE", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_VOICE);
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, false, CA_VOICE, CA_VOICEME, "DEVOICE", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_DEVOICE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "DEVOICE", CHAN_DEVOICE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_DEVOICE);
+ }
+};
+
+class CommandCSHalfOp : public Command
+{
+ public:
+ CommandCSHalfOp() : Command("HALFOP", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_HALFOP);
+
+ if (!cm)
+ {
+ return MOD_CONT;
+ }
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, true, CA_HALFOP, CA_HALFOPME, "HALFOP", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_HALFOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "HALFOP", CHAN_HALFOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_HALFOP);
+ }
+};
+
+class CommandCSDeHalfOp : public Command
+{
+ public:
+ CommandCSDeHalfOp() : Command("DEHALFOP", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_HALFOP);
+
+ if (!cm)
+ return MOD_CONT;
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, false, CA_HALFOP, CA_HALFOPME, "DEHALFOP", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_DEHALFOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "DEHALFOP", CHAN_DEHALFOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_DEHALFOP);
+ }
+};
+
+class CommandCSProtect : public Command
+{
+ public:
+ CommandCSProtect() : Command("PROTECT", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PROTECT);
+
+ if (!cm)
+ return MOD_CONT;
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, true, CA_PROTECT, CA_PROTECTME, "PROTECT", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_PROTECT);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "PROTECT", CHAN_PROTECT_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_PROTECT);
+ }
+};
+
+class CommandCSDeProtect : public Command
+{
+ public:
+ CommandCSDeProtect() : Command("DEPROTECT", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PROTECT);
+
+ if (!cm)
+ return MOD_CONT;
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, false, CA_PROTECT, CA_PROTECTME, "DEPROTECT", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_DEPROTECT);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "DEPROTECT", CHAN_DEPROTECT_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_DEPROTECT);
+ }
+};
+
+class CommandCSOwner : public Command
+{
+ public:
+ CommandCSOwner() : Command("OWNER", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OWNER);
+
+ if (!cm)
+ return MOD_CONT;
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, true, CA_OWNER, CA_OWNERME, "OWNER", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_OWNER);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "OWNER", CHAN_OWNER_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_OWNER);
+ }
+};
+
+class CommandCSDeOwner : public Command
+{
+ public:
+ CommandCSDeOwner() : Command("DEOWNER", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_OWNER);
+
+ if (!cm)
+ return MOD_CONT;
+
+ return do_util(u, cm, params.size() > 0 ? params[0].c_str() : NULL, params.size() > 1 ? params[1].c_str() : NULL, false, CA_OWNER, CA_OWNERME, "DEOWNER", CI_BEGIN);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_DEOWNER);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "DEOWNER", CHAN_DEOWNER_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_DEOWNER);
+ }
+};
+
+class CSModes : public Module
+{
+ public:
+ CSModes(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSOp());
+ this->AddCommand(ChanServ, new CommandCSDeOp());
+ this->AddCommand(ChanServ, new CommandCSVoice());
+ this->AddCommand(ChanServ, new CommandCSDeVoice());
+
+ if (Me && Me->IsSynced())
+ OnUplinkSync(NULL);
+
+ Implementation i[] = {I_OnUplinkSync, I_OnServerDisconnect};
+ ModuleManager::Attach(i, this, 2);
+ }
+
+ void OnUplinkSync(Server *)
+ {
+ if (ModeManager::FindChannelModeByName(CMODE_OWNER))
+ {
+ this->AddCommand(ChanServ, new CommandCSOwner());
+ this->AddCommand(ChanServ, new CommandCSDeOwner());
+ }
+
+ if (ModeManager::FindChannelModeByName(CMODE_PROTECT))
+ {
+ this->AddCommand(ChanServ, new CommandCSProtect());
+ this->AddCommand(ChanServ, new CommandCSDeProtect());
+ }
+
+ if (ModeManager::FindChannelModeByName(CMODE_HALFOP))
+ {
+ this->AddCommand(ChanServ, new CommandCSHalfOp());
+ this->AddCommand(ChanServ, new CommandCSDeHalfOp());
+ }
+ }
+
+ void OnServerDisconnect()
+ {
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "OWNER"));
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "DEOWNER"));
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "PROTECT"));
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "DEPROTECT"));
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "HALFOP"));
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "DEHALFOP"));
+ }
+};
+
+MODULE_INIT(CSModes)
diff --git a/modules/core/cs_register.cpp b/modules/core/cs_register.cpp
new file mode 100644
index 000000000..7bc146409
--- /dev/null
+++ b/modules/core/cs_register.cpp
@@ -0,0 +1,131 @@
+/* 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 CommandCSRegister : public Command
+{
+ public:
+ CommandCSRegister() : Command("REGISTER", 2, 2)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTEREDCHANNEL);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ const char *desc = params[1].c_str();
+ Channel *c = findchan(chan);
+ ChannelInfo *ci;
+ ChannelMode *cm;
+
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_REGISTER_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (*chan == '&')
+ notice_lang(Config.s_ChanServ, u, CHAN_REGISTER_NOT_LOCAL);
+ else if (*chan != '#')
+ notice_lang(Config.s_ChanServ, u, CHAN_SYMBOL_REQUIRED);
+ else if (!ircdproto->IsChannelValid(chan))
+ notice_lang(Config.s_ChanServ, u, CHAN_X_INVALID, chan);
+ else if ((ci = cs_findchan(chan)))
+ notice_lang(Config.s_ChanServ, u, CHAN_ALREADY_REGISTERED, chan);
+ else if (c && !c->HasUserStatus(u, CMODE_OP))
+ notice_lang(Config.s_ChanServ, u, CHAN_MUST_BE_CHANOP);
+ else if (Config.CSMaxReg && u->Account()->channelcount >= Config.CSMaxReg && !u->Account()->HasPriv("chanserv/no-register-limit"))
+ notice_lang(Config.s_ChanServ, u, u->Account()->channelcount > Config.CSMaxReg ? CHAN_EXCEEDED_CHANNEL_LIMIT : CHAN_REACHED_CHANNEL_LIMIT, Config.CSMaxReg);
+ else if (!(ci = new ChannelInfo(chan)))
+ {
+ Alog() << Config.s_ChanServ << ": makechan() failed for REGISTER " << chan;
+ notice_lang(Config.s_ChanServ, u, CHAN_REGISTRATION_FAILED);
+ }
+ else
+ {
+ if (c)
+ {
+ c->ci = ci;
+ ci->c = c;
+ }
+ ci->founder = u->Account();
+ ci->desc = sstrdup(desc);
+
+ if (c && c->topic)
+ {
+ ci->last_topic = sstrdup(c->topic);
+ ci->last_topic_setter = c->topic_setter;
+ ci->last_topic_time = c->topic_time;
+ }
+ else
+ ci->last_topic_setter = Config.s_ChanServ;
+
+ ci->bi = NULL;
+ ++ci->founder->channelcount;
+ Alog() << Config.s_ChanServ << ": Channel '" << chan << "' registered by " << u->GetMask();
+ notice_lang(Config.s_ChanServ, u, CHAN_REGISTERED, chan, u->nick.c_str());
+
+ /* Implement new mode lock */
+ 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);
+
+ /* Mark the channel as persistant */
+ if (c->HasMode(CMODE_PERM))
+ ci->SetFlag(CI_PERSIST);
+ /* Persist may be in def cflags, set it here */
+ else if (ci->HasFlag(CI_PERSIST) && (cm = ModeManager::FindChannelModeByName(CMODE_PERM)))
+ c->SetMode(NULL, CMODE_PERM);
+ }
+
+ FOREACH_MOD(I_OnChanRegistered, OnChanRegistered(ci));
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_REGISTER, Config.s_ChanServ);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "REGISTER", CHAN_REGISTER_SYNTAX);
+ }
+
+ void OnSyntaxError(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_REGISTER);
+ }
+};
+
+class CSRegister : public Module
+{
+ public:
+ CSRegister(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSRegister());
+ }
+};
+
+MODULE_INIT(CSRegister)
diff --git a/modules/core/cs_saset.cpp b/modules/core/cs_saset.cpp
new file mode 100644
index 000000000..161bf6f7c
--- /dev/null
+++ b/modules/core/cs_saset.cpp
@@ -0,0 +1,122 @@
+/* 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 CommandCSSASet : public Command
+{
+ std::map<ci::string, Command *> subcommands;
+
+ public:
+ CommandCSSASet(const ci::string &cname) : Command(cname, 2, 3)
+ {
+ }
+
+ ~CommandCSSASet()
+ {
+ for (std::map<ci::string, Command *>::const_iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ delete it->second;
+ this->subcommands.clear();
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_DISABLED);
+ return MOD_CONT;
+ }
+
+ Command *c = this->FindCommand(params[1]);
+
+ if (c)
+ {
+ ci::string cmdparams = cs_findchan(params[0])->name.c_str();
+ for (std::vector<ci::string>::const_iterator it = params.begin() + 2, it_end = params.end(); it != it_end; ++it)
+ cmdparams += " " + *it;
+ mod_run_cmd(ChanServ, u, c, params[1], cmdparams);
+ }
+ else
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_UNKNOWN_OPTION, params[1].c_str());
+ notice_lang(Config.s_ChanServ, u, MORE_INFO, Config.s_ChanServ, "SET");
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SASET_HEAD);
+ for (std::map<ci::string, Command *>::iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ it->second->OnServHelp(u);
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_TAIL);
+ return true;
+ }
+ else
+ {
+ Command *c = this->FindCommand(subcommand);
+
+ if (c)
+ return c->OnHelp(u, subcommand);
+ }
+
+ return false;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET", CHAN_SASET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SASET);
+ }
+
+ bool AddSubcommand(Command *c)
+ {
+ return this->subcommands.insert(std::make_pair(c->name, c)).second;
+ }
+
+ bool DelSubcommand(const ci::string &command)
+ {
+ return this->subcommands.erase(command);
+ }
+
+ Command *FindCommand(const ci::string &subcommand)
+ {
+ std::map<ci::string, Command *>::const_iterator it = this->subcommands.find(subcommand);
+
+ if (it != this->subcommands.end())
+ return it->second;
+
+ return NULL;
+ }
+};
+
+class CSSASet : public Module
+{
+ public:
+ CSSASet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSSASet("SASET"));
+ }
+};
+
+MODULE_INIT(CSSASet)
diff --git a/modules/core/cs_saset_noexpire.cpp b/modules/core/cs_saset_noexpire.cpp
new file mode 100644
index 000000000..bbed7f014
--- /dev/null
+++ b/modules/core/cs_saset_noexpire.cpp
@@ -0,0 +1,82 @@
+/* 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 CommandCSSASetNoexpire : public Command
+{
+ public:
+ CommandCSSASetNoexpire(const ci::string &cname) : Command(cname, 2, 2, "chanserv/saset/noexpire")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[0] == "ON")
+ {
+ ci->SetFlag(CI_NO_EXPIRE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_NOEXPIRE_ON, ci->name.c_str());
+ }
+ else if (params[0] == "OFF")
+ {
+ ci->UnsetFlag(CI_NO_EXPIRE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_NOEXPIRE_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "NOEXPIRE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_SERVADMIN_HELP_SET_NOEXPIRE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET NOEXPIRE", CHAN_SET_NOEXPIRE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_NOEXPIRE);
+ }
+};
+
+class CSSetNoexpire : public Module
+{
+ public:
+ CSSetNoexpire(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetNoexpire("NOEXPIRE"));
+ }
+
+ ~CSSetNoexpire()
+ {
+ Command *c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("NOEXPIRE");
+ }
+};
+
+MODULE_INIT(CSSetNoexpire)
diff --git a/modules/core/cs_set.cpp b/modules/core/cs_set.cpp
new file mode 100644
index 000000000..c9331e6a3
--- /dev/null
+++ b/modules/core/cs_set.cpp
@@ -0,0 +1,127 @@
+/* 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 CommandCSSet : public Command
+{
+ std::map<ci::string, Command *> subcommands;
+
+ public:
+ CommandCSSet(const ci::string &cname) : Command(cname, 2, 3)
+ {
+ }
+
+ ~CommandCSSet()
+ {
+ for (std::map<ci::string, Command *>::const_iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ delete it->second;
+ this->subcommands.clear();
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_DISABLED);
+ return MOD_CONT;
+ }
+ if (!check_access(u, cs_findchan(params[0]), CA_SET))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ Command *c = this->FindCommand(params[1]);
+
+ if (c)
+ {
+ ci::string cmdparams = cs_findchan(params[0])->name.c_str();
+ for (std::vector<ci::string>::const_iterator it = params.begin() + 2, it_end = params.end(); it != it_end; ++it)
+ cmdparams += " " + *it;
+ mod_run_cmd(ChanServ, u, c, params[1], cmdparams);
+ }
+ else
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_UNKNOWN_OPTION, params[1].c_str());
+ notice_lang(Config.s_ChanServ, u, MORE_INFO, Config.s_ChanServ, "SET");
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_HEAD);
+ for (std::map<ci::string, Command *>::iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ it->second->OnServHelp(u);
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_TAIL);
+ return true;
+ }
+ else
+ {
+ Command *c = this->FindCommand(subcommand);
+
+ if (c)
+ return c->OnHelp(u, subcommand);
+ }
+
+ return false;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET);
+ }
+
+ bool AddSubcommand(Command *c)
+ {
+ return this->subcommands.insert(std::make_pair(c->name, c)).second;
+ }
+
+ bool DelSubcommand(const ci::string &command)
+ {
+ return this->subcommands.erase(command);
+ }
+
+ Command *FindCommand(const ci::string &subcommand)
+ {
+ std::map<ci::string, Command *>::const_iterator it = this->subcommands.find(subcommand);
+
+ if (it != this->subcommands.end())
+ return it->second;
+
+ return NULL;
+ }
+};
+
+class CSSet : public Module
+{
+ public:
+ CSSet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSSet("SET"));
+ }
+};
+
+MODULE_INIT(CSSet)
diff --git a/modules/core/cs_set_bantype.cpp b/modules/core/cs_set_bantype.cpp
new file mode 100644
index 000000000..aad7841e6
--- /dev/null
+++ b/modules/core/cs_set_bantype.cpp
@@ -0,0 +1,110 @@
+/* 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 CommandCSSetBanType : public Command
+{
+ public:
+ CommandCSSetBanType(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ char *endptr;
+
+ int16 bantype = strtol(params[1].c_str(), &endptr, 10);
+
+ if (*endptr || bantype < 0 || bantype > 3)
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_BANTYPE_INVALID, params[1].c_str());
+ else
+ {
+ ci->bantype = bantype;
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_BANTYPE_CHANGED, ci->name.c_str(), ci->bantype);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_BANTYPE, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_BANTYPE);
+ }
+};
+
+class CommandCSSASetBanType : public CommandCSSetBanType
+{
+ public:
+ CommandCSSASetBanType(const ci::string &cname) : CommandCSSetBanType(cname, "chanserv/saset/bantype")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_BANTYPE, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SASET", CHAN_SASET_SYNTAX);
+ }
+};
+
+class CSSetBanType : public Module
+{
+ public:
+ CSSetBanType(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetBanType("BANTYPE"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetBanType("BANTYPE"));
+ }
+
+ ~CSSetBanType()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("BANTYPE");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("BANTYPE");
+ }
+};
+
+MODULE_INIT(CSSetBanType)
diff --git a/modules/core/cs_set_description.cpp b/modules/core/cs_set_description.cpp
new file mode 100644
index 000000000..57209ce2e
--- /dev/null
+++ b/modules/core/cs_set_description.cpp
@@ -0,0 +1,104 @@
+/* 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 CommandCSSetDescription : public Command
+{
+ public:
+ CommandCSSetDescription(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (ci->desc)
+ delete [] ci->desc;
+ ci->desc = sstrdup(params[1].c_str());
+
+ notice_lang(Config.s_ChanServ, u, CHAN_DESC_CHANGED, ci->name.c_str(), ci->desc);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_DESC, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_DESC);
+ }
+};
+
+class CommandCSSASetDescription : public CommandCSSetDescription
+{
+ public:
+ CommandCSSASetDescription(const ci::string &cname) : CommandCSSetDescription(cname, "chanserv/saset/description")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_DESC, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SASET", CHAN_SASET_SYNTAX);
+ }
+};
+
+class CSSetDescription : public Module
+{
+ public:
+ CSSetDescription(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetDescription("DESC"));
+
+ c = FindCommand(ChanServ, "SASEt");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetDescription("DESC"));
+ }
+
+ ~CSSetDescription()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("DESC");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("DESC");
+ }
+};
+
+MODULE_INIT(CSSetDescription)
diff --git a/modules/core/cs_set_entrymsg.cpp b/modules/core/cs_set_entrymsg.cpp
new file mode 100644
index 000000000..d2ec2bccc
--- /dev/null
+++ b/modules/core/cs_set_entrymsg.cpp
@@ -0,0 +1,111 @@
+/* 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 CommandCSSetEntryMsg : public Command
+{
+ public:
+ CommandCSSetEntryMsg(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 1, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (ci->entry_message)
+ delete [] ci->entry_message;
+ if (params.size() > 1)
+ {
+ ci->entry_message = sstrdup(params[1].c_str());
+ notice_lang(Config.s_ChanServ, u, CHAN_ENTRY_MSG_CHANGED, ci->name.c_str(), ci->entry_message);
+ }
+ else
+ {
+ ci->entry_message = NULL;
+ notice_lang(Config.s_ChanServ, u, CHAN_ENTRY_MSG_UNSET, ci->name.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_ENTRYMSG, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_ENTRYMSG);
+ }
+};
+
+class CommandCSSASetEntryMsg : public CommandCSSetEntryMsg
+{
+ public:
+ CommandCSSASetEntryMsg(const ci::string &cname) : CommandCSSetEntryMsg(cname, "/chanserv/saset/entrymsg")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_ENTRYMSG, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SASET", CHAN_SASET_SYNTAX);
+ }
+};
+
+class CSSetEntryMsg : public Module
+{
+ public:
+ CSSetEntryMsg(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetEntryMsg("ENTRYMSG"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetEntryMsg("ENTRYMSG"));
+ }
+
+ ~CSSetEntryMsg()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("ENTRYMSG");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("ENTRYMSG");
+ }
+};
+
+MODULE_INIT(CSSetEntryMsg)
diff --git a/modules/core/cs_set_founder.cpp b/modules/core/cs_set_founder.cpp
new file mode 100644
index 000000000..35422755e
--- /dev/null
+++ b/modules/core/cs_set_founder.cpp
@@ -0,0 +1,138 @@
+/* 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 CommandCSSetFounder : public Command
+{
+ public:
+ CommandCSSetFounder(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (this->permission.empty() && (ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER)))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ NickAlias *na = findnick(params[1]);
+ NickCore *nc, *nc0 = ci->founder;
+
+
+ if (!na)
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_REGISTERED, params[1].c_str());
+ return MOD_CONT;
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_FORBIDDEN, na->nick);
+ return MOD_CONT;
+ }
+
+ nc = na->nc;
+ if (Config.CSMaxReg && nc->channelcount >= Config.CSMaxReg && !u->Account()->HasPriv("chanserv/no-register-limit"))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_FOUNDER_TOO_MANY_CHANS, na->nick);
+ return MOD_CONT;
+ }
+
+ Alog() << Config.s_ChanServ << ": Changing founder of " << ci->name << " from " << ci->founder->display << " to " << nc->display << " by " << u->GetMask();
+
+ /* Founder and successor must not be the same group */
+ if (nc == ci->successor)
+ ci->successor = NULL;
+
+ --nc0->channelcount;
+ ci->founder = nc;
+ ++nc->channelcount;
+
+ notice_lang(Config.s_ChanServ, u, CHAN_FOUNDER_CHANGED, ci->name.c_str(), na->nick);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_FOUNDER, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_FOUNDER);
+ }
+};
+
+class CommandCSSASetFounder : public CommandCSSetFounder
+{
+ public:
+ CommandCSSASetFounder(const ci::string &cname) : CommandCSSetFounder(cname, "chanserv/saset/founder")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_FOUNDER, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SASET", CHAN_SASET_SYNTAX);
+ }
+};
+
+class CSSetFounder : public Module
+{
+ public:
+ CSSetFounder(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetFounder("FOUNDER"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetFounder("FOUNDER"));
+ }
+
+ ~CSSetFounder()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("FOUNDER");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("FOUNDER");
+ }
+};
+
+MODULE_INIT(CSSetFounder)
diff --git a/modules/core/cs_set_keeptopic.cpp b/modules/core/cs_set_keeptopic.cpp
new file mode 100644
index 000000000..10595373f
--- /dev/null
+++ b/modules/core/cs_set_keeptopic.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetKeepTopic : public Command
+{
+ public:
+ CommandCSSetKeepTopic(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_KEEPTOPIC);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_KEEPTOPIC_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_KEEPTOPIC);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_KEEPTOPIC_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "KEEPTOPIC");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_KEEPTOPIC, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET KEEPTOPIC", CHAN_SET_KEEPTOPIC_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_KEEPTOPIC);
+ }
+};
+
+class CommandCSSASetKeepTopic : public CommandCSSetKeepTopic
+{
+ public:
+ CommandCSSASetKeepTopic(const ci::string &cname) : CommandCSSetKeepTopic(cname, "chanserv/saset/keeptopic")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_KEEPTOPIC, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET KEEPTOPIC", CHAN_SASET_KEEPTOPIC_SYNTAX);
+ }
+};
+
+class CSSetKeepTopic : public Module
+{
+ public:
+ CSSetKeepTopic(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetKeepTopic("KEEPTOPIC"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetKeepTopic("KEEPTOPIC"));
+ }
+
+ ~CSSetKeepTopic()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("KEEPTOPIC");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("KEEPTOPIC");
+ }
+};
+
+MODULE_INIT(CSSetKeepTopic)
diff --git a/modules/core/cs_set_mlock.cpp b/modules/core/cs_set_mlock.cpp
new file mode 100644
index 000000000..5a31a8565
--- /dev/null
+++ b/modules/core/cs_set_mlock.cpp
@@ -0,0 +1,182 @@
+/* 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 CommandCSSetMLock : public Command
+{
+ public:
+ CommandCSSetMLock(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 1, 0, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */
+ unsigned char mode;
+ ChannelMode *cm;
+ unsigned paramcount = 2;
+
+ ci->ClearMLock();
+ ci->ClearParams();
+
+ if (ModeManager::FindChannelModeByName(CMODE_REGISTERED))
+ ci->SetMLock(CMODE_REGISTERED, true);
+
+ const char *modes = params[1].c_str();
+ while (modes && (mode = *modes++))
+ {
+ switch (mode)
+ {
+ case '+':
+ add = 1;
+ continue;
+ case '-':
+ add = 0;
+ continue;
+ default:
+ if (add < 0)
+ continue;
+ }
+
+ if ((cm = ModeManager::FindChannelModeByChar(mode)))
+ {
+ if (cm->Type == MODE_STATUS || cm->Type == MODE_LIST || !cm->CanSet(u))
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_MLOCK_IMPOSSIBLE_CHAR, mode);
+ else if (add)
+ {
+ ci->RemoveMLock(cm->Name);
+
+ if (cm->Type == MODE_PARAM)
+ {
+ if (paramcount >= params.size())
+ continue;
+
+ std::string param = params[paramcount].c_str();
+
+ ChannelModeParam *cmp = dynamic_cast<ChannelModeParam *>(cm);
+
+ if (!cmp || !cmp->IsValid(param))
+ continue;
+
+ ci->SetMLock(cmp->Name, true, param);
+ }
+ else
+ ci->SetMLock(cm->Name, true);
+ }
+ else
+ ci->SetMLock(cm->Name, false);
+ }
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_MLOCK_UNKNOWN_CHAR, mode);
+ } /* while (*modes) */
+
+ /* We can't mlock +L if +l is not mlocked as well. */
+ if (ModeManager::FindChannelModeByName(CMODE_REDIRECT) && ci->HasMLock(CMODE_REDIRECT, true) && !ci->HasMLock(CMODE_LIMIT, true))
+ {
+ ci->RemoveMLock(CMODE_REDIRECT);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_MLOCK_L_REQUIRED);
+ }
+
+ /* Some ircd we can't set NOKNOCK without INVITE */
+ /* So check if we need there is a NOKNOCK MODE and that we need INVITEONLY */
+ if (ModeManager::FindChannelModeByName(CMODE_NOKNOCK) && ircd->knock_needs_i && ci->HasMLock(CMODE_NOKNOCK, true) && !ci->HasMLock(CMODE_INVITE, true))
+ {
+ ci->RemoveMLock(CMODE_NOKNOCK);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_MLOCK_K_REQUIRED);
+ }
+
+ /* Since we always enforce mode r there is no way to have no
+ * mode lock at all.
+ */
+ if (get_mlock_modes(ci, 0))
+ notice_lang(Config.s_ChanServ, u, CHAN_MLOCK_CHANGED, ci->name.c_str(), get_mlock_modes(ci, 0));
+
+ /* Implement the new lock. */
+ if (ci->c)
+ check_modes(ci->c);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_MLOCK, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_MLOCK);
+ }
+};
+
+class CommandCSSASetMLock : public CommandCSSetMLock
+{
+ public:
+ CommandCSSASetMLock(const ci::string &cname) : CommandCSSetMLock(cname, "chanserv/saset/mlock")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_MLOCK, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SASET", CHAN_SASET_SYNTAX);
+ }
+};
+
+class CSSetMLock : public Module
+{
+ public:
+ CSSetMLock(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetMLock("MLOCK"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetMLock("MLOCK"));
+ }
+
+ ~CSSetMLock()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("MLOCK");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("MLOCK");
+ }
+};
+
+MODULE_INIT(CSSetMLock)
diff --git a/modules/core/cs_set_opnotice.cpp b/modules/core/cs_set_opnotice.cpp
new file mode 100644
index 000000000..79e953ef2
--- /dev/null
+++ b/modules/core/cs_set_opnotice.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetOpNotice : public Command
+{
+ public:
+ CommandCSSetOpNotice(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_OPNOTICE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_OPNOTICE_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_OPNOTICE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_OPNOTICE_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "OPNOTICE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_OPNOTICE, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET OPNOTICE", CHAN_SET_OPNOTICE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_OPNOTICE);
+ }
+};
+
+class CommandCSSASetOpNotice : public CommandCSSetOpNotice
+{
+ public:
+ CommandCSSASetOpNotice(const ci::string &cname) : CommandCSSetOpNotice(cname, "chanserv/saset/opnotice")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_OPNOTICE, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET OPNOTICE", CHAN_SASET_OPNOTICE_SYNTAX);
+ }
+};
+
+class CSSetOpNotice : public Module
+{
+ public:
+ CSSetOpNotice(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetOpNotice("OPNOTICE"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetOpNotice("OPNOTICE"));
+ }
+
+ ~CSSetOpNotice()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("OPNOTICE");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("OPNOTICE");
+ }
+};
+
+MODULE_INIT(CSSetOpNotice)
diff --git a/modules/core/cs_set_peace.cpp b/modules/core/cs_set_peace.cpp
new file mode 100644
index 000000000..3dadcb5e3
--- /dev/null
+++ b/modules/core/cs_set_peace.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetPeace : public Command
+{
+ public:
+ CommandCSSetPeace(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_PEACE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_PEACE_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_PEACE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_PEACE_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "PEACE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_PEACE, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET PEACE", CHAN_SET_PEACE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_PEACE, "SET");
+ }
+};
+
+class CommandCSSASetPeace : public CommandCSSetPeace
+{
+ public:
+ CommandCSSASetPeace(const ci::string &cname) : CommandCSSetPeace(cname, "chanserv/saset/peace")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_PEACE, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET PEACE", CHAN_SASET_PEACE_SYNTAX);
+ }
+};
+
+class CSSetPeace : public Module
+{
+ public:
+ CSSetPeace(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetPeace("PEACE"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetPeace("PEACE"));
+ }
+
+ ~CSSetPeace()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("PEACE");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("PEACE");
+ }
+};
+
+MODULE_INIT(CSSetPeace)
diff --git a/modules/core/cs_set_persist.cpp b/modules/core/cs_set_persist.cpp
new file mode 100644
index 000000000..d03e3874d
--- /dev/null
+++ b/modules/core/cs_set_persist.cpp
@@ -0,0 +1,154 @@
+/* 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 CommandCSSetPersist : public Command
+{
+ public:
+ CommandCSSetPersist(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ ChannelMode *cm = ModeManager::FindChannelModeByName(CMODE_PERM);
+
+ if (params[0] == "ON")
+ {
+ if (!ci->HasFlag(CI_PERSIST))
+ {
+ ci->SetFlag(CI_PERSIST);
+
+ /* Channel doesn't exist, create it internally */
+ if (!ci->c)
+ {
+ Channel *c = new Channel(ci->name);
+ if (ci->bi)
+ ci->bi->Join(c);
+ }
+
+ /* No botserv bot, no channel mode */
+ /* Give them ChanServ
+ * Yes, this works fine with no Config.s_BotServ
+ */
+ if (!ci->bi && !cm)
+ ChanServ->Assign(NULL, ci);
+
+ /* Set the perm mode */
+ if (cm && ci->c && !ci->c->HasMode(CMODE_PERM))
+ ci->c->SetMode(NULL, cm);
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_PERSIST_ON, ci->name.c_str());
+ }
+ else if (params[0] == "OFF")
+ {
+ if (ci->HasFlag(CI_PERSIST))
+ {
+ ci->UnsetFlag(CI_PERSIST);
+
+ /* Unset perm mode */
+ if (cm && ci->c && ci->c->HasMode(CMODE_PERM))
+ ci->c->RemoveMode(NULL, cm);
+ if (Config.s_BotServ && ci->bi && ci->c->FindUser(ci->bi))
+ ci->bi->Part(ci->c);
+
+ /* No channel mode, no BotServ, but using ChanServ as the botserv bot
+ * which was assigned when persist was set on
+ */
+ if (!cm && !Config.s_BotServ && ci->bi)
+ /* Unassign bot */
+ ChanServ->UnAssign(NULL, ci);
+
+ if (ci->c && ci->c->users.empty())
+ delete ci->c;
+ }
+
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_PERSIST_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "PERSIST");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_PERSIST, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET PERSIST", CHAN_SET_PERSIST_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_PERSIST);
+ }
+};
+
+class CommandCSSASetPersist : public CommandCSSetPersist
+{
+ public:
+ CommandCSSASetPersist(const ci::string &cname) : CommandCSSetPersist(cname, "chanserv/saset/persist")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_PERSIST, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET PERSIST", CHAN_SASET_PERSIST_SYNTAX);
+ }
+};
+
+class CSSetPersist : public Module
+{
+ public:
+ CSSetPersist(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetPersist("PERSIST"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetPersist("PERSIST"));
+ }
+
+ ~CSSetPersist()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("PERSIST");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("PERSIST");
+ }
+};
+
+MODULE_INIT(CSSetPersist)
diff --git a/modules/core/cs_set_private.cpp b/modules/core/cs_set_private.cpp
new file mode 100644
index 000000000..ed618c6ca
--- /dev/null
+++ b/modules/core/cs_set_private.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetPrivate : public Command
+{
+ public:
+ CommandCSSetPrivate(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_PRIVATE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_PRIVATE_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_PRIVATE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_PRIVATE_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "PRIVATE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_PRIVATE, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET PRIVATE", CHAN_SET_PRIVATE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_PRIVATE);
+ }
+};
+
+class CommandCSSASetPrivate : public CommandCSSetPrivate
+{
+ public:
+ CommandCSSASetPrivate(const ci::string &cname) : CommandCSSetPrivate(cname, "chanserv/saset/private")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_PRIVATE, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET PRIVATE", CHAN_SASET_PRIVATE_SYNTAX);
+ }
+};
+
+class CSSetPrivate : public Module
+{
+ public:
+ CSSetPrivate(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetPrivate("PRIVATE"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetPrivate("PRIVATE"));
+ }
+
+ ~CSSetPrivate()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("PRIVATE");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("PRIVATE");
+ }
+};
+
+MODULE_INIT(CSSetPrivate)
diff --git a/modules/core/cs_set_restricted.cpp b/modules/core/cs_set_restricted.cpp
new file mode 100644
index 000000000..5d9537443
--- /dev/null
+++ b/modules/core/cs_set_restricted.cpp
@@ -0,0 +1,112 @@
+/* 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 CommandCSSetRestricted : public Command
+{
+ public:
+ CommandCSSetRestricted(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_RESTRICTED);
+ if (ci->levels[CA_NOJOIN] < 0)
+ ci->levels[CA_NOJOIN] = 0;
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_RESTRICTED_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_RESTRICTED);
+ if (ci->levels[CA_NOJOIN] >= 0)
+ ci->levels[CA_NOJOIN] = -2;
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_RESTRICTED_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "RESTRICTED");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_RESTRICTED, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET RESTRICTED", CHAN_SET_RESTRICTED_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_RESTRICTED);
+ }
+};
+
+class CommandCSSASetRestricted : public CommandCSSetRestricted
+{
+ public:
+ CommandCSSASetRestricted(const ci::string &cname) : CommandCSSetRestricted(cname, "chanserv/saset/restricted")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_RESTRICTED, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET RESTRICTED", CHAN_SASET_RESTRICTED_SYNTAX);
+ }
+};
+
+class CSSetRestricted : public Module
+{
+ public:
+ CSSetRestricted(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetRestricted("RESTRICTED"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetRestricted("RESTRICTED"));
+ }
+
+ ~CSSetRestricted()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("RESTRICTED");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("RESTRICTED");
+ }
+};
+
+MODULE_INIT(CSSetRestricted)
diff --git a/modules/core/cs_set_secure.cpp b/modules/core/cs_set_secure.cpp
new file mode 100644
index 000000000..d5f26fcba
--- /dev/null
+++ b/modules/core/cs_set_secure.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetSecure : public Command
+{
+ public:
+ CommandCSSetSecure(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_SECURE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SECURE_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_SECURE);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SECURE_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "SECURE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SECURE, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET SECURE", CHAN_SET_SECURE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_SECURE);
+ }
+};
+
+class CommandCSSASetSecure : public CommandCSSetSecure
+{
+ public:
+ CommandCSSASetSecure(const ci::string &cname) : CommandCSSetSecure(cname, "chanserv/saset/secure")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SECURE, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET SECURE", CHAN_SASET_SECURE_SYNTAX);
+ }
+};
+
+class CSSetSecure : public Module
+{
+ public:
+ CSSetSecure(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetSecure("SECURE"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetSecure("SECURE"));
+ }
+
+ ~CSSetSecure()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("SECURE");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("SECURE");
+ }
+};
+
+MODULE_INIT(CSSetSecure)
diff --git a/modules/core/cs_set_securefounder.cpp b/modules/core/cs_set_securefounder.cpp
new file mode 100644
index 000000000..cda7deef8
--- /dev/null
+++ b/modules/core/cs_set_securefounder.cpp
@@ -0,0 +1,115 @@
+/* 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 CommandCSSetSecureFounder : public Command
+{
+ public:
+ CommandCSSetSecureFounder(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (this->permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_SECUREFOUNDER);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SECUREFOUNDER_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_SECUREFOUNDER);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SECUREFOUNDER_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "SECUREFOUNDER");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SECUREFOUNDER, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET SECUREFOUNDER", CHAN_SET_SECUREFOUNDER_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_SECUREFOUNDER);
+ }
+};
+
+class CommandCSSASetSecureFounder : public CommandCSSetSecureFounder
+{
+ public:
+ CommandCSSASetSecureFounder(const ci::string &cname) : CommandCSSetSecureFounder(cname, "chanserv/saset/securefounder")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SECUREFOUNDER, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET SECUREFOUNDER", CHAN_SASET_SECUREFOUNDER_SYNTAX);
+ }
+};
+
+class CSSetSecureFounder : public Module
+{
+ public:
+ CSSetSecureFounder(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetSecureFounder("SECUREFOUNDER"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetSecureFounder("SECUREFOUNDER"));
+ }
+
+ ~CSSetSecureFounder()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("SECUREFOUNDER");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("SECUREFOUNDER");
+ }
+};
+
+MODULE_INIT(CSSetSecureFounder)
diff --git a/modules/core/cs_set_secureops.cpp b/modules/core/cs_set_secureops.cpp
new file mode 100644
index 000000000..8409018d2
--- /dev/null
+++ b/modules/core/cs_set_secureops.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetSecureOps : public Command
+{
+ public:
+ CommandCSSetSecureOps(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_SECUREOPS);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SECUREOPS_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_SECUREOPS);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SECUREOPS_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "SECUREOPS");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SECUREOPS, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET SECUREOPS", CHAN_SET_SECUREOPS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_SECUREOPS);
+ }
+};
+
+class CommandCSSASetSecureOps : public CommandCSSetSecureOps
+{
+ public:
+ CommandCSSASetSecureOps(const ci::string &cname) : CommandCSSetSecureOps(cname, "chanserv/saset/secureops")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SECUREOPS, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET SECUREOPS", CHAN_SASET_SECUREOPS_SYNTAX);
+ }
+};
+
+class CSSetSecureOps : public Module
+{
+ public:
+ CSSetSecureOps(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetSecureOps("SECUREOPS"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetSecureOps("SECUREOPS"));
+ }
+
+ ~CSSetSecureOps()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("SECUREOPS");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("SECUREOPS");
+ }
+};
+
+MODULE_INIT(CSSetSecureOps)
diff --git a/modules/core/cs_set_signkick.cpp b/modules/core/cs_set_signkick.cpp
new file mode 100644
index 000000000..b792ec9c9
--- /dev/null
+++ b/modules/core/cs_set_signkick.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetSignKick : public Command
+{
+ public:
+ CommandCSSetSignKick(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_SIGNKICK);
+ ci->UnsetFlag(CI_SIGNKICK_LEVEL);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SIGNKICK_ON, ci->name.c_str());
+ }
+ else if (params[1] == "LEVEL")
+ {
+ ci->SetFlag(CI_SIGNKICK_LEVEL);
+ ci->UnsetFlag(CI_SIGNKICK);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SIGNKICK_LEVEL, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_SIGNKICK);
+ ci->UnsetFlag(CI_SIGNKICK_LEVEL);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_SIGNKICK_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "SIGNKICK");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SIGNKICK, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET SIGNKICK", CHAN_SET_SIGNKICK_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_SIGNKICK);
+ }
+};
+
+class CommandCSSASetSignKick : public CommandCSSetSignKick
+{
+ public:
+ CommandCSSASetSignKick(const ci::string &cname) : CommandCSSetSignKick(cname, "chanserv/saset/signkick")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SIGNKICK, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET SIGNKICK", CHAN_SASET_SIGNKICK_SYNTAX);
+ }
+};
+
+class CSSetSignKick : public Module
+{
+ public:
+ CSSetSignKick(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetSignKick("SIGNKICK"));
+ }
+
+ ~CSSetSignKick()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("SIGNKICK");
+ }
+};
+
+MODULE_INIT(CSSetSignKick)
diff --git a/modules/core/cs_set_successor.cpp b/modules/core/cs_set_successor.cpp
new file mode 100644
index 000000000..f5517b8a9
--- /dev/null
+++ b/modules/core/cs_set_successor.cpp
@@ -0,0 +1,140 @@
+/* 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 CommandCSSetSuccessor : public Command
+{
+ public:
+ CommandCSSetSuccessor(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 1, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (this->permission.empty() && ci->HasFlag(CI_SECUREFOUNDER) ? !IsFounder(u, ci) : !check_access(u, ci, CA_FOUNDER))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ NickCore *nc;
+
+ if (params.size() > 1)
+ {
+ NickAlias *na = findnick(params[1]);
+
+ if (!na)
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_REGISTERED, params[1].c_str());
+ return MOD_CONT;
+ }
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_FORBIDDEN, na->nick);
+ return MOD_CONT;
+ }
+ if (na->nc == ci->founder)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_SUCCESSOR_IS_FOUNDER, na->nick, ci->name.c_str());
+ return MOD_CONT;
+ }
+ nc = na->nc;
+
+ }
+ else
+ nc = NULL;
+
+ Alog() << Config.s_ChanServ << ": Changing successor of " << ci->name << " from " << (ci->successor ? ci->successor->display : "none") << " to " << (nc ? nc->display : "none") << " by " << u->GetMask();
+
+ ci->successor = nc;
+
+ if (nc)
+ notice_lang(Config.s_ChanServ, u, CHAN_SUCCESSOR_CHANGED, ci->name.c_str(), nc->display);
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_SUCCESSOR_UNSET, ci->name.c_str());
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SUCCESSOR, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_SUCCESSOR);
+ }
+};
+
+class CommandCSSASetSuccessor : public CommandCSSetSuccessor
+{
+ public:
+ CommandCSSASetSuccessor(const ci::string &cname) : CommandCSSetSuccessor(cname, "chanserv/saset/successor")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_SUCCESSOR, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_ChanServ, u, "SASEt", CHAN_SASET_SYNTAX);
+ }
+};
+
+class CSSetSuccessor : public Module
+{
+ public:
+ CSSetSuccessor(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetSuccessor("SUCCESSOR"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetSuccessor("SUCCESSOR"));
+ }
+
+ ~CSSetSuccessor()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("SUCCESSOR");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("SUCCESSOR");
+ }
+};
+
+MODULE_INIT(CSSetSuccessor)
diff --git a/modules/core/cs_set_topiclock.cpp b/modules/core/cs_set_topiclock.cpp
new file mode 100644
index 000000000..5e89862f6
--- /dev/null
+++ b/modules/core/cs_set_topiclock.cpp
@@ -0,0 +1,109 @@
+/* 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 CommandCSSetTopicLock : public Command
+{
+ public:
+ CommandCSSetTopicLock(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ ci->SetFlag(CI_TOPICLOCK);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_TOPICLOCK_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_TOPICLOCK);
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_TOPICLOCK_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "TOPICLOCK");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_TOPICLOCK, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET", CHAN_SET_TOPICLOCK_SYNTAX);;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_TOPICLOCK);
+ }
+};
+
+class CommandCSSASetTopicLock : public CommandCSSetTopicLock
+{
+ public:
+ CommandCSSASetTopicLock(const ci::string &cname) : CommandCSSetTopicLock(cname, "chanserv/saset/topiclock")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_TOPICLOCK, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET", CHAN_SASET_TOPICLOCK_SYNTAX);
+ }
+};
+
+class CSSetTopicLock : public Module
+{
+ public:
+ CSSetTopicLock(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetTopicLock("TOPICLOCK"));
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandCSSASetTopicLock("TOPICLOCK"));
+ }
+
+ ~CSSetTopicLock()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("TOPICLOCK");
+
+ c = FindCommand(ChanServ, "SASET");
+ if (c)
+ c->DelSubcommand("TOPICLOCK");
+ }
+};
+
+MODULE_INIT(CSSetTopicLock)
diff --git a/modules/core/cs_set_xop.cpp b/modules/core/cs_set_xop.cpp
new file mode 100644
index 000000000..854989fba
--- /dev/null
+++ b/modules/core/cs_set_xop.cpp
@@ -0,0 +1,139 @@
+/* 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"
+
+#define CHECKLEV(lev) (ci->levels[(lev)] != ACCESS_INVALID && access->level >= ci->levels[(lev)])
+
+class CommandCSSetXOP : public Command
+{
+ public:
+ CommandCSSetXOP(const ci::string &cname, const ci::string &cpermission = "") : Command(cname, 2, 2, cpermission)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (!FindModule("cs_xop"))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_XOP_NOT_AVAILABLE, "XOP");
+ return MOD_CONT;
+ }
+
+ ChannelInfo *ci = cs_findchan(params[0]);
+ assert(ci);
+
+ if (params[1] == "ON")
+ {
+ if (!ci->HasFlag(CI_XOP))
+ {
+ ChanAccess *access;
+
+ for (unsigned i = ci->GetAccessCount() - 1; 0 <= i; --i)
+ {
+ access = ci->GetAccess(i);
+
+ /* This will probably cause wrong levels to be set, but hey,
+ * it's better than losing it altogether.
+ */
+ if (access->level == ACCESS_QOP)
+ access->level = ACCESS_QOP;
+ else if (CHECKLEV(CA_AKICK) || CHECKLEV(CA_SET))
+ access->level = ACCESS_SOP;
+ else if (CHECKLEV(CA_AUTOOP) || CHECKLEV(CA_OPDEOP) || CHECKLEV(CA_OPDEOPME))
+ access->level = ACCESS_AOP;
+ else if (ModeManager::FindChannelModeByName(CMODE_HALFOP) && (CHECKLEV(CA_AUTOHALFOP) || CHECKLEV(CA_HALFOP) || CHECKLEV(CA_HALFOPME)))
+ access->level = ACCESS_HOP;
+ else if (CHECKLEV(CA_AUTOVOICE) || CHECKLEV(CA_VOICE) || CHECKLEV(CA_VOICEME))
+ access->level = ACCESS_VOP;
+ else
+ ci->EraseAccess(i);
+ }
+
+ reset_levels(ci);
+ ci->SetFlag(CI_XOP);
+ }
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " enabled XOP for " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_XOP_ON, ci->name.c_str());
+ }
+ else if (params[1] == "OFF")
+ {
+ ci->UnsetFlag(CI_XOP);
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " disabled XOP for " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_SET_XOP_OFF, ci->name.c_str());
+ }
+ else
+ this->OnSyntaxError(u, "XOP");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_XOP, "SET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SET XOP", CHAN_SET_XOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SET_XOP);
+ }
+};
+
+class CommandCSSASetXOP : public CommandCSSetXOP
+{
+ public:
+ CommandCSSASetXOP(const ci::string &cname) : CommandCSSetXOP(cname, "chanserv/saset/xop")
+ {
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SET_XOP, "SASET");
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_ChanServ, u, "SASET XOP", CHAN_SASET_XOP_SYNTAX);
+ }
+};
+
+class CSSetXOP : public Module
+{
+ public:
+ CSSetXOP(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandCSSetXOP("XOP"));
+ }
+
+ ~CSSetXOP()
+ {
+ Command *c = FindCommand(ChanServ, "SET");
+ if (c)
+ c->DelSubcommand("XOP");
+ }
+};
+
+MODULE_INIT(CSSetXOP)
diff --git a/modules/core/cs_status.cpp b/modules/core/cs_status.cpp
new file mode 100644
index 000000000..31dab055c
--- /dev/null
+++ b/modules/core/cs_status.cpp
@@ -0,0 +1,74 @@
+/* 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 CommandCSStatus : public Command
+{
+ public:
+ CommandCSStatus() : Command("STATUS", 2, 2, "chanserv/status")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ChannelInfo *ci;
+ User *u2;
+ const char *chan = params[0].c_str();
+ const char *nick = params[1].c_str();
+ const char *temp = NULL;
+
+ if (!(ci = cs_findchan(chan)))
+ {
+ temp = chan;
+ chan = nick;
+ nick = temp;
+ ci = cs_findchan(chan);
+ }
+ if (!ci)
+ notice_lang(Config.s_ChanServ, u, CHAN_STATUS_NOT_REGGED, temp);
+ else if ((u2 = finduser(nick)))
+ notice_lang(Config.s_ChanServ, u, CHAN_STATUS_INFO, chan, nick, get_access(u2, ci));
+ else /* !u2 */
+ notice_lang(Config.s_ChanServ, u, CHAN_STATUS_NOTONLINE, nick);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_SERVADMIN_HELP_STATUS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "STATUS", CHAN_STATUS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_STATUS);
+ }
+};
+
+class CSStatus : public Module
+{
+ public:
+ CSStatus(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ }
+};
+
+MODULE_INIT(CSStatus)
diff --git a/modules/core/cs_suspend.cpp b/modules/core/cs_suspend.cpp
new file mode 100644
index 000000000..8878e5552
--- /dev/null
+++ b/modules/core/cs_suspend.cpp
@@ -0,0 +1,195 @@
+/* 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 CommandCSSuspend : public Command
+{
+ public:
+ CommandCSSuspend() : Command("SUSPEND", 1, 2, "chanserv/suspend")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ const char *reason = params.size() > 1 ? params[1].c_str() : NULL;
+ ChannelInfo *ci = cs_findchan(chan);
+
+ Channel *c;
+
+ /* Assumes that permission checking has already been done. */
+ if (Config.ForceForbidReason && !reason)
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ if (chan[0] != '#')
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_UNSUSPEND_ERROR);
+ return MOD_CONT;
+ }
+
+ /* You should not SUSPEND a FORBIDEN channel */
+ if (ci->HasFlag(CI_FORBIDDEN))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_MAY_NOT_BE_REGISTERED, chan);
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ notice_lang(Config.s_ChanServ, u, READ_ONLY_MODE);
+
+ if (ci)
+ {
+ ci->SetFlag(CI_SUSPENDED);
+ ci->forbidby = sstrdup(u->nick.c_str());
+ if (reason)
+ ci->forbidreason = sstrdup(reason);
+
+ if ((c = findchan(ci->name)))
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
+ {
+ UserContainer *uc = *it++;
+
+ if (is_oper(uc->user))
+ continue;
+
+ c->Kick(NULL, uc->user, "%s", reason ? reason : getstring(uc->user->Account(), CHAN_SUSPEND_REASON));
+ }
+ }
+
+ if (Config.WallForbid)
+ ircdproto->SendGlobops(ChanServ, "\2%s\2 used SUSPEND on channel \2%s\2", u->nick.c_str(), ci->name.c_str());
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " set SUSPEND for channel " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_SUSPEND_SUCCEEDED, chan);
+
+ FOREACH_MOD(I_OnChanSuspend, OnChanSuspend(ci));
+ }
+ else
+ {
+ Alog() << Config.s_ChanServ << ": Valid SUSPEND for " << ci->name << " by " << u->GetMask() << " failed";
+ notice_lang(Config.s_ChanServ, u, CHAN_SUSPEND_FAILED, chan);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_SERVADMIN_HELP_SUSPEND);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "SUSPEND", Config.ForceForbidReason ? CHAN_SUSPEND_SYNTAX_REASON : CHAN_SUSPEND_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SUSPEND);
+ }
+};
+
+class CommandCSUnSuspend : public Command
+{
+ public:
+ CommandCSUnSuspend() : Command("UNSUSPEND", 1, 1, "chanserv/suspend")
+ {
+ this->SetFlag(CFLAG_ALLOW_SUSPENDED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (chan[0] != '#')
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_UNSUSPEND_ERROR);
+ return MOD_CONT;
+ }
+ if (readonly)
+ notice_lang(Config.s_ChanServ, u, READ_ONLY_MODE);
+
+ /* Only UNSUSPEND already suspended channels */
+ if (!(ci->HasFlag(CI_SUSPENDED)))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_UNSUSPEND_FAILED, chan);
+ return MOD_CONT;
+ }
+
+ if (ci)
+ {
+ ci->UnsetFlag(CI_SUSPENDED);
+ if (ci->forbidreason)
+ {
+ delete [] ci->forbidreason;
+ ci->forbidreason = NULL;
+ }
+ if (ci->forbidby)
+ {
+ delete [] ci->forbidby;
+ ci->forbidby = NULL;
+ }
+
+ if (Config.WallForbid)
+ ircdproto->SendGlobops(ChanServ, "\2%s\2 used UNSUSPEND on channel \2%s\2", u->nick.c_str(), ci->name.c_str());
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " set UNSUSPEND for channel " << ci->name;
+ notice_lang(Config.s_ChanServ, u, CHAN_UNSUSPEND_SUCCEEDED, chan);
+
+ FOREACH_MOD(I_OnChanUnsuspend, OnChanUnsuspend(ci));
+ }
+ else
+ {
+ Alog() << Config.s_ChanServ << ": Valid UNSUSPEND for " << chan << " by " << u->nick << " failed";
+ notice_lang(Config.s_ChanServ, u, CHAN_UNSUSPEND_FAILED, chan);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_SERVADMIN_HELP_UNSUSPEND);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "UNSUSPEND", CHAN_UNSUSPEND_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_UNSUSPEND);
+ }
+};
+
+class CSSuspend : public Module
+{
+ public:
+ CSSuspend(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSSuspend());
+ this->AddCommand(ChanServ, new CommandCSUnSuspend());
+ }
+};
+
+MODULE_INIT(CSSuspend)
diff --git a/modules/core/cs_topic.cpp b/modules/core/cs_topic.cpp
new file mode 100644
index 000000000..f408eaedf
--- /dev/null
+++ b/modules/core/cs_topic.cpp
@@ -0,0 +1,98 @@
+/* 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 CommandCSTopic : public Command
+{
+ public:
+ CommandCSTopic() : Command("TOPIC", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ const char *topic = params.size() > 1 ? params[1].c_str() : NULL;
+
+ Channel *c;
+ ChannelInfo *ci;
+
+ if ((c = findchan(chan)))
+ ci = c->ci;
+
+ if (!c)
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ else if (!check_access(u, ci, CA_TOPIC) && !u->Account()->HasCommand("chanserv/topic"))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else
+ {
+ if (ci->last_topic)
+ delete [] ci->last_topic;
+ ci->last_topic = topic ? sstrdup(topic) : NULL;
+ ci->last_topic_setter = u->nick;
+ ci->last_topic_time = time(NULL);
+
+ if (c->topic)
+ delete [] c->topic;
+ c->topic = topic ? sstrdup(topic) : NULL;
+ c->topic_setter = u->nick;
+ if (ircd->topictsbackward)
+ c->topic_time = c->topic_time - 1;
+ else
+ c->topic_time = ci->last_topic_time;
+
+ if (!check_access(u, ci, CA_TOPIC))
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " changed topic of " << c->name << " as services admin.";
+ if (ircd->join2set && whosends(ci) == ChanServ)
+ {
+ ChanServ->Join(c);
+ ircdproto->SendMode(NULL, c, "+o %s", Config.s_ChanServ); // XXX
+ }
+ ircdproto->SendTopic(whosends(ci), c, u->nick.c_str(), topic ? topic : "");
+ if (ircd->join2set && whosends(ci) == ChanServ)
+ ChanServ->Part(c);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_TOPIC);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "TOPIC", CHAN_TOPIC_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_TOPIC);
+ }
+};
+
+class CSTopic : public Module
+{
+ public:
+ CSTopic(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSTopic());
+ }
+};
+
+MODULE_INIT(CSTopic)
diff --git a/modules/core/cs_unban.cpp b/modules/core/cs_unban.cpp
new file mode 100644
index 000000000..9085d46a1
--- /dev/null
+++ b/modules/core/cs_unban.cpp
@@ -0,0 +1,88 @@
+/* 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 CommandCSUnban : public Command
+{
+ public:
+ CommandCSUnban() : Command("UNBAN", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ Channel *c;
+ User *u2;
+
+ if (!(c = findchan(chan)))
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_X_NOT_IN_USE, chan);
+ return MOD_CONT;
+ }
+
+ if (!check_access(u, c->ci, CA_UNBAN))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ u2 = u;
+ if (params.size() > 1)
+ u2 = finduser(params[1]);
+
+ if (!u2)
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_IN_USE, params[1].c_str());
+ return MOD_CONT;
+ }
+
+ common_unban(c->ci, u2->nick);
+ if (u2 == u)
+ notice_lang(Config.s_ChanServ, u, CHAN_UNBANNED, c->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, CHAN_UNBANNED_OTHER, u2->nick.c_str(), c->name.c_str());
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_UNBAN);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "UNBAN", CHAN_UNBAN_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_UNBAN);
+ }
+};
+
+class CSUnban : public Module
+{
+ public:
+ CSUnban(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSUnban());
+ }
+};
+
+MODULE_INIT(CSUnban)
diff --git a/modules/core/cs_xop.cpp b/modules/core/cs_xop.cpp
new file mode 100644
index 000000000..b4c5cd24d
--- /dev/null
+++ b/modules/core/cs_xop.cpp
@@ -0,0 +1,661 @@
+/* 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"
+
+enum
+{
+ XOP_AOP,
+ XOP_SOP,
+ XOP_VOP,
+ XOP_HOP,
+ XOP_QOP,
+ XOP_TYPES
+};
+
+enum
+{
+ XOP_DISABLED,
+ XOP_NICKS_ONLY,
+ XOP_ADDED,
+ XOP_MOVED,
+ XOP_NO_SUCH_ENTRY,
+ XOP_NOT_FOUND,
+ XOP_NO_MATCH,
+ XOP_DELETED,
+ XOP_DELETED_ONE,
+ XOP_DELETED_SEVERAL,
+ XOP_LIST_EMPTY,
+ XOP_LIST_HEADER,
+ XOP_CLEAR,
+ XOP_MESSAGES
+};
+
+int xop_msgs[XOP_TYPES][XOP_MESSAGES] = {
+ {CHAN_AOP_DISABLED,
+ CHAN_AOP_NICKS_ONLY,
+ CHAN_AOP_ADDED,
+ CHAN_AOP_MOVED,
+ CHAN_AOP_NO_SUCH_ENTRY,
+ CHAN_AOP_NOT_FOUND,
+ CHAN_AOP_NO_MATCH,
+ CHAN_AOP_DELETED,
+ CHAN_AOP_DELETED_ONE,
+ CHAN_AOP_DELETED_SEVERAL,
+ CHAN_AOP_LIST_EMPTY,
+ CHAN_AOP_LIST_HEADER,
+ CHAN_AOP_CLEAR},
+ {CHAN_SOP_DISABLED,
+ CHAN_SOP_NICKS_ONLY,
+ CHAN_SOP_ADDED,
+ CHAN_SOP_MOVED,
+ CHAN_SOP_NO_SUCH_ENTRY,
+ CHAN_SOP_NOT_FOUND,
+ CHAN_SOP_NO_MATCH,
+ CHAN_SOP_DELETED,
+ CHAN_SOP_DELETED_ONE,
+ CHAN_SOP_DELETED_SEVERAL,
+ CHAN_SOP_LIST_EMPTY,
+ CHAN_SOP_LIST_HEADER,
+ CHAN_SOP_CLEAR},
+ {CHAN_VOP_DISABLED,
+ CHAN_VOP_NICKS_ONLY,
+ CHAN_VOP_ADDED,
+ CHAN_VOP_MOVED,
+ CHAN_VOP_NO_SUCH_ENTRY,
+ CHAN_VOP_NOT_FOUND,
+ CHAN_VOP_NO_MATCH,
+ CHAN_VOP_DELETED,
+ CHAN_VOP_DELETED_ONE,
+ CHAN_VOP_DELETED_SEVERAL,
+ CHAN_VOP_LIST_EMPTY,
+ CHAN_VOP_LIST_HEADER,
+ CHAN_VOP_CLEAR},
+ {CHAN_HOP_DISABLED,
+ CHAN_HOP_NICKS_ONLY,
+ CHAN_HOP_ADDED,
+ CHAN_HOP_MOVED,
+ CHAN_HOP_NO_SUCH_ENTRY,
+ CHAN_HOP_NOT_FOUND,
+ CHAN_HOP_NO_MATCH,
+ CHAN_HOP_DELETED,
+ CHAN_HOP_DELETED_ONE,
+ CHAN_HOP_DELETED_SEVERAL,
+ CHAN_HOP_LIST_EMPTY,
+ CHAN_HOP_LIST_HEADER,
+ CHAN_HOP_CLEAR},
+ {CHAN_QOP_DISABLED,
+ CHAN_QOP_NICKS_ONLY,
+ CHAN_QOP_ADDED,
+ CHAN_QOP_MOVED,
+ CHAN_QOP_NO_SUCH_ENTRY,
+ CHAN_QOP_NOT_FOUND,
+ CHAN_QOP_NO_MATCH,
+ CHAN_QOP_DELETED,
+ CHAN_QOP_DELETED_ONE,
+ CHAN_QOP_DELETED_SEVERAL,
+ CHAN_QOP_LIST_EMPTY,
+ CHAN_QOP_LIST_HEADER,
+ CHAN_QOP_CLEAR}
+};
+
+class XOPListCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ int level;
+ int *messages;
+ bool SentHeader;
+ public:
+ XOPListCallback(User *_u, ChannelInfo *_ci, const std::string &numlist, int _level, int *_messages) : NumberList(numlist, false), u(_u), ci(_ci), level(_level), messages(_messages), SentHeader(false)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAccessCount())
+ return;
+
+ ChanAccess *access = ci->GetAccess(Number - 1);
+
+ if (level != access->level)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, messages[XOP_LIST_HEADER], ci->name.c_str());
+ }
+
+ DoList(u, ci, access, Number - 1, level, messages);
+ }
+
+ static void DoList(User *u, ChannelInfo *ci, ChanAccess *access, unsigned index, int level, int *messages)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_XOP_LIST_FORMAT, index, access->nc->display);
+ }
+};
+
+class XOPDelCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ int *messages;
+ unsigned Deleted;
+ std::string Nicks;
+ public:
+ XOPDelCallback(User *_u, ChannelInfo *_ci, int *_messages, const std::string &numlist) : NumberList(numlist, true), u(_u), ci(_ci), messages(_messages), Deleted(0)
+ {
+ }
+
+ ~XOPDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_ChanServ, u, messages[XOP_NO_MATCH], ci->name.c_str());
+ else
+ {
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << get_access(u, ci) << ") deleted access of users " << Nicks << " on " << ci->name;
+
+ if (Deleted == 1)
+ notice_lang(Config.s_ChanServ, u, messages[XOP_DELETED_ONE], ci->name.c_str());
+ else
+ notice_lang(Config.s_ChanServ, u, messages[XOP_DELETED_SEVERAL], Deleted, ci->name.c_str());
+ }
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > ci->GetAccessCount())
+ return;
+
+ ChanAccess *access = ci->GetAccess(Number - 1);
+
+ ++Deleted;
+ if (!Nicks.empty())
+ Nicks += ", " + std::string(access->nc->display);
+ else
+ Nicks = access->nc->display;
+
+ FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, access->nc));
+
+ ci->EraseAccess(Number - 1);
+ }
+};
+
+class XOPBase : public Command
+{
+ private:
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params, ChannelInfo *ci, int level, int *messages)
+ {
+ const char *nick = params.size() > 2 ? params[2].c_str() : NULL;
+ ChanAccess *access;
+ int change = 0;
+
+ if (!nick)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_DISABLED]);
+ return MOD_CONT;
+ }
+
+ short ulev = get_access(u, ci);
+
+ if ((level >= ulev || ulev < ACCESS_AOP) && !u->Account()->HasPriv("chanserv/access/modify"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ NickAlias *na = findnick(nick);
+ if (!na)
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_NICKS_ONLY]);
+ return MOD_CONT;
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_FORBIDDEN, na->nick);
+ return MOD_CONT;
+ }
+
+ NickCore *nc = na->nc;
+ access = ci->GetAccess(nc);
+ if (access)
+ {
+ /**
+ * Patch provided by PopCorn to prevert AOP's reducing SOP's levels
+ **/
+ if (access->level >= ulev && !u->Account()->HasPriv("chanserv/access/modify"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ ++change;
+ }
+
+ if (!change && ci->GetAccessCount() >= Config.CSAccessMax)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_XOP_REACHED_LIMIT, Config.CSAccessMax);
+ return MOD_CONT;
+ }
+
+ if (!change)
+ ci->AddAccess(nc, level, u->nick);
+ else
+ {
+ access->level = level;
+ access->last_seen = 0;
+ access->creator = u->nick;
+ }
+
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << ulev << ") " << (change ? "changed" : "set") << " access level " << level << " to " << na->nick << " (group " << nc->display << ") on channel " << ci->name;
+
+ if (!change)
+ {
+ FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, u, nc, level));
+ notice_lang(Config.s_ChanServ, u, messages[XOP_ADDED], nc->display, ci->name.c_str());
+ }
+ else
+ {
+ FOREACH_MOD(I_OnAccessChange, OnAccessChange(ci, u, na->nc, level));
+ notice_lang(Config.s_ChanServ, u, messages[XOP_MOVED], nc->display, ci->name.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params, ChannelInfo *ci, int level, int *messages)
+ {
+ const char *nick = params.size() > 2 ? params[2].c_str() : NULL;
+ ChanAccess *access;
+
+ if (!nick)
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_DISABLED]);
+ return MOD_CONT;
+ }
+
+ if (!ci->GetAccessCount())
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_LIST_EMPTY], ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ short ulev = get_access(u, ci);
+
+ if ((level >= ulev || ulev < ACCESS_AOP) && !u->Account()->HasPriv("chanserv/access/modify"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ /* Special case: is it a number/list? Only do search if it isn't. */
+ if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick))
+ {
+ XOPDelCallback list(u, ci, messages, nick);
+ list.Process();
+ }
+ else
+ {
+ NickAlias *na = findnick(nick);
+ if (!na)
+ {
+ notice_lang(Config.s_ChanServ, u, NICK_X_NOT_REGISTERED, nick);
+ return MOD_CONT;
+ }
+ NickCore *nc = na->nc;
+
+ unsigned i, end;
+ for (i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ access = ci->GetAccess(nc, level);
+
+ if (access->nc == nc)
+ break;
+ }
+
+ if (i == end)
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_NOT_FOUND], nick, ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ if (ulev <= access->level && !u->Account()->HasPriv("chanserv/access/modify"))
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ else
+ {
+ Alog() << Config.s_ChanServ << ": " << u->GetMask() << " (level " << get_access(u, ci) << ") deleted access of user " << access->nc->display << " on " << ci->name;
+
+ notice_lang(Config.s_ChanServ, u, messages[XOP_DELETED], access->nc->display, ci->name.c_str());
+
+ FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, u, na->nc));
+
+ ci->EraseAccess(i);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, const std::vector<ci::string> &params, ChannelInfo *ci, int level, int *messages)
+ {
+ const char *nick = params.size() > 2 ? params[2].c_str() : NULL;
+
+ if (!get_access(u, ci) && !u->Account()->HasCommand("chanserv/access/list"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (!ci->GetAccessCount())
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_LIST_EMPTY], ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ if (nick && strspn(nick, "1234567890,-") == strlen(nick))
+ {
+ XOPListCallback list(u, ci, nick, level, messages);
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
+ {
+ ChanAccess *access = ci->GetAccess(i);
+
+ if (nick && access->nc && !Anope::Match(access->nc->display, nick, false))
+ continue;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_ChanServ, u, messages[XOP_LIST_HEADER], ci->name.c_str());
+ }
+
+ XOPListCallback::DoList(u, ci, access, i, level, messages);
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_ChanServ, u, messages[XOP_NO_MATCH], ci->name.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoClear(User *u, const std::vector<ci::string> &params, ChannelInfo *ci, int level, int *messages)
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_DISABLED]);
+ return MOD_CONT;
+ }
+
+ if (!ci->GetAccessCount())
+ {
+ notice_lang(Config.s_ChanServ, u, messages[XOP_LIST_EMPTY], ci->name.c_str());
+ return MOD_CONT;
+ }
+
+ if (!check_access(u, ci, CA_FOUNDER) && !u->Account()->HasPriv("chanserv/access/modify"))
+ {
+ notice_lang(Config.s_ChanServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ for (unsigned i = ci->GetAccessCount(); i > 0; --i)
+ {
+ ChanAccess *access = ci->GetAccess(i - 1);
+ if (access->level == level)
+ ci->EraseAccess(i - 1);
+ }
+
+ FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, u));
+
+ notice_lang(Config.s_ChanServ, u, messages[XOP_CLEAR], ci->name.c_str());
+
+ return MOD_CONT;
+ }
+ protected:
+ CommandReturn DoXop(User *u, const std::vector<ci::string> &params, int level, int *messages)
+ {
+ const char *chan = params[0].c_str();
+ ci::string cmd = params[1];
+
+ ChannelInfo *ci = cs_findchan(chan);
+
+ if (!(ci->HasFlag(CI_XOP)))
+ notice_lang(Config.s_ChanServ, u, CHAN_XOP_ACCESS, Config.s_ChanServ);
+ else if (cmd == "ADD")
+ return this->DoAdd(u, params, ci, level, messages);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params, ci, level, messages);
+ else if (cmd == "LIST")
+ return this->DoList(u, params, ci, level, messages);
+ else if (cmd == "CLEAR")
+ return this->DoClear(u, params, ci, level, messages);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+ public:
+ XOPBase(const ci::string &command) : Command(command, 2, 3)
+ {
+ }
+
+ virtual ~XOPBase()
+ {
+ }
+
+ virtual CommandReturn Execute(User *u, const std::vector<ci::string> &params) = 0;
+
+ virtual bool OnHelp(User *u, const ci::string &subcommand) = 0;
+
+ virtual void OnSyntaxError(User *u, const ci::string &subcommand) = 0;
+
+ virtual void OnServHelp(User *u) = 0;
+};
+
+class CommandCSQOP : public XOPBase
+{
+ public:
+ CommandCSQOP() : XOPBase("QOP")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoXop(u, params, ACCESS_QOP, xop_msgs[XOP_QOP]);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_QOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "QOP", CHAN_QOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_QOP);
+ }
+};
+
+class CommandCSAOP : public XOPBase
+{
+ public:
+ CommandCSAOP() : XOPBase("AOP")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoXop(u, params, ACCESS_AOP, xop_msgs[XOP_AOP]);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_AOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "AOP", CHAN_AOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_AOP);
+ }
+};
+
+class CommandCSHOP : public XOPBase
+{
+ public:
+ CommandCSHOP() : XOPBase("HOP")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoXop(u, params, ACCESS_HOP, xop_msgs[XOP_HOP]);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_HOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "HOP", CHAN_HOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_HOP);
+ }
+};
+
+class CommandCSSOP : public XOPBase
+{
+ public:
+ CommandCSSOP() : XOPBase("SOP")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoXop(u, params, ACCESS_SOP, xop_msgs[XOP_SOP]);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_SOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "SOP", CHAN_SOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_SOP);
+ }
+};
+
+class CommandCSVOP : public XOPBase
+{
+ public:
+ CommandCSVOP() : XOPBase("VOP")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoXop(u, params, ACCESS_VOP, xop_msgs[XOP_VOP]);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_ChanServ, u, CHAN_HELP_VOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_ChanServ, u, "VOP", CHAN_VOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_ChanServ, u, CHAN_HELP_CMD_VOP);
+ }
+};
+
+class CSXOP : public Module
+{
+ public:
+ CSXOP(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(ChanServ, new CommandCSSOP());
+ this->AddCommand(ChanServ, new CommandCSAOP());
+ this->AddCommand(ChanServ, new CommandCSVOP());
+
+ if (Me && Me->IsSynced())
+ OnUplinkSync(NULL);
+
+ Implementation i[] = {I_OnUplinkSync, I_OnServerDisconnect};
+ ModuleManager::Attach(i, this, 2);
+ }
+
+ void OnUplinkSync(Server *)
+ {
+ if (ModeManager::FindChannelModeByName(CMODE_OWNER))
+ this->AddCommand(ChanServ, new CommandCSQOP());
+ if (ModeManager::FindChannelModeByName(CMODE_HALFOP))
+ this->AddCommand(ChanServ, new CommandCSHOP());
+ }
+
+ void OnServerDisconnect()
+ {
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "QOP"));
+ this->DelCommand(ChanServ, FindCommand(ChanServ, "HOP"));
+ }
+};
+
+MODULE_INIT(CSXOP)
diff --git a/modules/core/db_plain.cpp b/modules/core/db_plain.cpp
new file mode 100644
index 000000000..885faf41b
--- /dev/null
+++ b/modules/core/db_plain.cpp
@@ -0,0 +1,1166 @@
+/*
+ * (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"
+
+std::fstream db;
+std::string DatabaseFile;
+
+/** Enum used for what METADATA type we are reading
+ */
+enum MDType
+{
+ MD_NONE,
+ MD_NC,
+ MD_NA,
+ MD_NR,
+ MD_BI,
+ MD_CH
+};
+
+/* Character for newlines, in my research I have concluded it is significantly faster
+ * to use \n instead of std::endl because std::endl also flushes the buffer, which
+ * is not necessary here
+ */
+static const char endl = '\n';
+
+/** Read from the database and call the events
+ * @param m If specified it only calls the read events for the specific module
+ */
+static void ReadDatabase(Module *m = NULL)
+{
+ EventReturn MOD_RESULT;
+ MDType Type = MD_NONE;
+
+ db.clear();
+ db.open(DatabaseFile.c_str(), std::ios_base::in);
+
+ if (!db.is_open())
+ {
+ Alog() << "Unable to open " << DatabaseFile << " for reading!";
+ return;
+ }
+
+ NickCore *nc = NULL;
+ NickAlias *na = NULL;
+ NickRequest *nr = NULL;
+ BotInfo *bi = NULL;
+ ChannelInfo *ci = NULL;
+
+ std::string buf;
+ while (std::getline(db, buf))
+ {
+ if (buf.empty())
+ continue;
+
+ spacesepstream sep(buf);
+ std::vector<std::string> params;
+ while (sep.GetToken(buf))
+ {
+ if (buf[0] == ':')
+ {
+ buf.erase(buf.begin());
+ if (!buf.empty() && !sep.StreamEnd())
+ params.push_back(buf + " " + sep.GetRemaining());
+ else if (!sep.StreamEnd())
+ params.push_back(sep.GetRemaining());
+ else if (!buf.empty())
+ params.push_back(buf);
+ break;
+ }
+ else
+ params.push_back(buf);
+ }
+
+ if (m)
+ MOD_RESULT = m->OnDatabaseRead(params);
+ else
+ FOREACH_RESULT(I_OnDatabaseRead, OnDatabaseRead(params));
+ if (MOD_RESULT == EVENT_STOP)
+ continue;
+
+ if (!params.empty())
+ {
+ if (params[0] == "NC")
+ {
+ nc = findcore(params[1]);
+ Type = MD_NC;
+ }
+ else if (params[0] == "NA")
+ {
+ na = findnick(params[2]);
+ Type = MD_NA;
+ }
+ else if (params[0] == "NR")
+ {
+ nr = findrequestnick(params[1]);
+ Type = MD_NR;
+ }
+ else if (params[0] == "BI")
+ {
+ bi = findbot(params[1]);
+ Type = MD_BI;
+ }
+ else if (params[0] == "CH")
+ {
+ ci = cs_findchan(params[1]);
+ Type = MD_CH;
+ }
+ else if (params[0] == "MD")
+ {
+ std::string key = params[1];
+ params.erase(params.begin());
+ params.erase(params.begin());
+
+ if (Type == MD_NC && nc)
+ {
+ try
+ {
+ if (m)
+ m->OnDatabaseReadMetadata(nc, key, params);
+ else
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nc, key, params));
+ }
+ catch (DatabaseException& ex)
+ {
+ Alog() << "[db_plain]: " << ex.GetReason();
+ }
+ }
+ else if (Type == MD_NA && na)
+ {
+ try
+ {
+ if (m)
+ m->OnDatabaseReadMetadata(na, key, params);
+ else
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(na, key, params));
+ }
+ catch (DatabaseException& ex)
+ {
+ Alog() << "[db_plain]: " << ex.GetReason();
+ }
+ }
+ else if (Type == MD_NR && nr)
+ {
+ try
+ {
+ if (m)
+ m->OnDatabaseReadMetadata(nr, key, params);
+ else
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(nr, key, params));
+ }
+ catch (DatabaseException& ex)
+ {
+ Alog() << "[db_plain]: " << ex.GetReason();
+ }
+ }
+ else if (Type == MD_BI && bi)
+ {
+ try
+ {
+ if (m)
+ m->OnDatabaseReadMetadata(bi, key, params);
+ else
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(bi, key, params));
+ }
+ catch (DatabaseException& ex)
+ {
+ Alog() << "[db_plain]: " << ex.GetReason();
+ }
+ }
+ else if (Type == MD_CH && ci)
+ {
+ try
+ {
+ if (m)
+ m->OnDatabaseReadMetadata(ci, key, params);
+ else
+ FOREACH_RESULT(I_OnDatabaseReadMetadata, OnDatabaseReadMetadata(ci, key, params));
+ }
+ catch (DatabaseException& ex)
+ {
+ Alog() << "[db_plain]: " << ex.GetReason();
+ if (!ci->founder)
+ {
+ delete ci;
+ ci = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ db.close();
+}
+
+struct LangInfo
+{
+ std::string Name;
+ int LanguageId;
+};
+
+struct ChannelFlagInfo
+{
+ std::string Name;
+ ChannelInfoFlag Flag;
+};
+
+struct ChannelLevel
+{
+ std::string Name;
+ int Level;
+};
+
+struct BotFlagInfo
+{
+ std::string Name;
+ BotServFlag Flag;
+};
+
+struct NickCoreFlagInfo
+{
+ std::string Name;
+ NickCoreFlag Flag;
+};
+
+LangInfo LangInfos[] = {
+ {"en", LANG_EN_US},
+ {"es", LANG_ES},
+ {"pt", LANG_PT},
+ {"fr", LANG_FR},
+ {"tr", LANG_TR},
+ {"it", LANG_IT},
+ {"de", LANG_DE},
+ {"ca", LANG_CAT},
+ {"gr", LANG_GR},
+ {"nl", LANG_NL},
+ {"ru", LANG_RU},
+ {"hu", LANG_HUN},
+ {"pl", LANG_PL},
+ {"", -1}
+};
+
+ChannelFlagInfo ChannelInfoFlags[] = {
+ {"KEEPTOPIC", CI_KEEPTOPIC},
+ {"SECUREOPS", CI_SECUREOPS},
+ {"PRIVATE", CI_PRIVATE},
+ {"TOPICLOCK", CI_TOPICLOCK},
+ {"RESTRICTED", CI_RESTRICTED},
+ {"PEACE", CI_PEACE},
+ {"SECURE", CI_SECURE},
+ {"FORBIDDEN", CI_FORBIDDEN},
+ {"NO_EXPIRE", CI_NO_EXPIRE},
+ {"MEMO_HARDMAX", CI_MEMO_HARDMAX},
+ {"OPNOTICE", CI_OPNOTICE},
+ {"SECUREFOUNDER", CI_SECUREFOUNDER},
+ {"SIGNKICK", CI_SIGNKICK},
+ {"SIGNKICK_LEVEL", CI_SIGNKICK_LEVEL},
+ {"XOP", CI_XOP},
+ {"SUSPENDED", CI_SUSPENDED},
+ {"PERSIST", CI_PERSIST},
+ {"", static_cast<ChannelInfoFlag>(-1)}
+};
+
+ChannelLevel ChannelLevels[] = {
+ {"INVITE", CA_INVITE},
+ {"AKICK", CA_AKICK},
+ {"SET", CA_SET},
+ {"UNBAN", CA_UNBAN},
+ {"AUTOOP", CA_AUTOOP},
+ {"AUTODEOP", CA_AUTODEOP},
+ {"AUTOVOICE", CA_AUTOVOICE},
+ {"OPDEOP", CA_OPDEOP},
+ {"ACCESS_LIST", CA_ACCESS_LIST},
+ {"CLEAR", CA_CLEAR},
+ {"NOJOIN", CA_NOJOIN},
+ {"ACCESS_CHANGE", CA_ACCESS_CHANGE},
+ {"MEMO", CA_MEMO},
+ {"ASSIGN", CA_ASSIGN},
+ {"BADWORDS", CA_BADWORDS},
+ {"NOKICK", CA_NOKICK},
+ {"FANTASIA", CA_FANTASIA},
+ {"SAY", CA_SAY},
+ {"GREET", CA_GREET},
+ {"VOICEME", CA_VOICEME},
+ {"VOICE", CA_VOICE},
+ {"GETKEY", CA_GETKEY},
+ {"AUTOHALFOP", CA_AUTOHALFOP},
+ {"AUTOPROTECT", CA_AUTOPROTECT},
+ {"OPDEOPME", CA_OPDEOPME},
+ {"HALFOPME", CA_HALFOPME},
+ {"HALFOP", CA_HALFOP},
+ {"PROTECTME", CA_PROTECTME},
+ {"PROTECT", CA_PROTECT},
+ {"KICKME", CA_KICKME},
+ {"KICK", CA_KICK},
+ {"SIGNKICK", CA_SIGNKICK},
+ {"BANME", CA_BANME},
+ {"BAN", CA_BAN},
+ {"TOPIC", CA_TOPIC},
+ {"INFO", CA_INFO},
+ {"AUTOOWNER", CA_AUTOOWNER},
+ {"OWNER", CA_OWNER},
+ {"OWNERME", CA_OWNERME},
+ {"", -1}
+};
+
+BotFlagInfo BotFlags[] = {
+ {"DONTKICKOPS", BS_DONTKICKOPS},
+ {"DONTKICKVOICES", BS_DONTKICKVOICES},
+ {"FANTASY", BS_FANTASY},
+ {"SYMBIOSIS", BS_SYMBIOSIS},
+ {"GREET", BS_GREET},
+ {"NOBOT", BS_NOBOT},
+ {"KICK_BOLDS", BS_KICK_BOLDS},
+ {"KICK_COLORS", BS_KICK_COLORS},
+ {"KICK_REVERSES", BS_KICK_REVERSES},
+ {"KICK_UNDERLINES", BS_KICK_UNDERLINES},
+ {"KICK_BADWORDS", BS_KICK_BADWORDS},
+ {"KICK_CAPS", BS_KICK_CAPS},
+ {"KICK_FLOOD", BS_KICK_FLOOD},
+ {"KICK_REPEAT", BS_KICK_REPEAT},
+ {"", static_cast<BotServFlag>(-1)}
+};
+
+NickCoreFlagInfo NickCoreFlags[] = {
+ {"KILLPROTECT", NI_KILLPROTECT},
+ {"SECURE", NI_SECURE},
+ {"MSG", NI_MSG},
+ {"MEMO_HARDMAX", NI_MEMO_HARDMAX},
+ {"MEMO_SIGNON", NI_MEMO_SIGNON},
+ {"MEMO_RECEIVE", NI_MEMO_RECEIVE},
+ {"PRIVATE", NI_PRIVATE},
+ {"HIDE_EMAIL", NI_HIDE_EMAIL},
+ {"HIDE_MASK", NI_HIDE_MASK},
+ {"HIDE_QUIT", NI_HIDE_QUIT},
+ {"KILL_QUICK", NI_KILL_QUICK},
+ {"KILL_IMMED", NI_KILL_IMMED},
+ {"MEMO_MAIL", NI_MEMO_MAIL},
+ {"HIDE_STATUS", NI_HIDE_STATUS},
+ {"SUSPENDED", NI_SUSPENDED},
+ {"AUTOOP", NI_AUTOOP},
+ {"FORBIDDEN", NI_FORBIDDEN},
+ {"", static_cast<NickCoreFlag>(-1)}
+};
+
+static void LoadNickCore(const std::vector<std::string> &params)
+{
+ NickCore *nc = new NickCore(params[0]);
+ /* Clear default flags */
+ nc->ClearFlags();
+
+ if (params.size() <= 2)
+ /* This is a forbidden nick */
+ return;
+
+ nc->pass.assign(params[1]);
+
+ for (int i = 0; LangInfos[i].LanguageId != -1; ++i)
+ if (params[2] == LangInfos[i].Name)
+ nc->language = LangInfos[i].LanguageId;
+
+ nc->memos.memomax = atoi(params[3].c_str());
+ nc->channelcount = atoi(params[4].c_str());
+
+ Alog(LOG_DEBUG_2) << "[db_plain]: Loaded NickCore " << nc->display;
+}
+
+static void LoadNickAlias(const std::vector<std::string> &params)
+{
+ NickCore *nc = findcore(params[0]);
+ if (!nc)
+ {
+ Alog() << "[db_plain]: Unable to find core " << params[0];
+ return;
+ }
+
+ NickAlias *na = new NickAlias(params[1], nc);
+
+ na->time_registered = strtol(params[2].c_str(), NULL, 10);
+
+ na->last_seen = strtol(params[3].c_str(), NULL, 10);
+
+ Alog(LOG_DEBUG_2) << "[db_plain}: Loaded nickalias for " << na->nick;
+}
+
+static void LoadNickRequest(const std::vector<std::string> &params)
+{
+ NickRequest *nr = new NickRequest(params[0]);
+ nr->passcode = params[1];
+ nr->password = params[2];
+ nr->email = sstrdup(params[3].c_str());
+ nr->requested = atol(params[4].c_str());
+
+ Alog(LOG_DEBUG_2) << "[db_plain]: Loaded nickrequest for " << nr->nick;
+}
+
+static void LoadBotInfo(const std::vector<std::string> &params)
+{
+ BotInfo *bi = findbot(params[0]);
+ if (!bi)
+ bi = new BotInfo(params[0]);
+ bi->SetIdent(params[1]);
+ bi->host = sstrdup(params[2].c_str());
+ bi->created = atol(params[3].c_str());
+ //bi no longer has a chancount, use bi->chans.size()
+ //bi->chancount = atol(params[4].c_str());
+ bi->realname = sstrdup(params[5].c_str());
+
+ Alog(LOG_DEBUG_2) << "[db_plain]: Loaded botinfo for " << bi->nick;
+}
+
+static void LoadChanInfo(const std::vector<std::string> &params)
+{
+ ChannelInfo *ci = new ChannelInfo(params[0]);
+ /* CLear default mlock */
+ ci->ClearMLock();
+ /* Remove default channel flags */
+ ci->ClearFlags();
+ ci->botflags.ClearFlags();
+
+ ci->time_registered = strtol(params[1].c_str(), NULL, 10);
+
+ ci->last_used = strtol(params[2].c_str(), NULL, 10);
+
+ ci->bantype = atoi(params[3].c_str());
+
+ ci->memos.memomax = atoi(params[4].c_str());
+
+ Alog(LOG_DEBUG_2) << "[db_plain]: loaded channel " << ci->name;
+}
+
+static void LoadOperInfo(const std::vector<std::string> &params)
+{
+ if (params[0] == "STATS")
+ {
+ maxusercnt = atol(params[1].c_str());
+ maxusertime = strtol(params[2].c_str(), NULL, 10);
+ }
+ else if (params[0] == "SNLINE" || params[0] == "SQLINE" || params[0] == "SZLINE")
+ {
+ ci::string mask = params[1].c_str();
+ ci::string by = params[2].c_str();
+ time_t seton = atol(params[3].c_str());
+ time_t expires = atol(params[4].c_str());
+ std::string reason = params[5];
+
+ XLine *x = NULL;
+ if (params[0] == "SNLINE" && SNLine)
+ x = SNLine->Add(NULL, NULL, mask, expires, reason);
+ else if (params[0] == "SQLINE" && SQLine)
+ x = SQLine->Add(NULL, NULL, mask, expires, reason);
+ else if (params[0] == "SZLINE" && SZLine)
+ x = SZLine->Add(NULL, NULL, mask, expires, reason);
+ if (x)
+ {
+ x->By = by;
+ x->Created = seton;
+ }
+ }
+ else if (params[0] == "AKILL" && SGLine)
+ {
+ ci::string user = params[1].c_str();
+ ci::string host = params[2].c_str();
+ ci::string by = params[3].c_str();
+ time_t seton = atol(params[4].c_str());
+ time_t expires = atol(params[5].c_str());
+ std::string reason = params[6];
+
+ XLine *x = SGLine->Add(NULL, NULL, user + "@" + host, expires, reason);
+ if (x)
+ {
+ x->By = by;
+ x->Created = seton;
+ }
+ }
+ else if (params[0] == "EXCEPTION")
+ {
+ ++nexceptions;
+ exceptions = static_cast<Exception *>(srealloc(exceptions, sizeof(Exception) * nexceptions));
+ exceptions[nexceptions - 1].mask = sstrdup(params[1].c_str());
+ exceptions[nexceptions - 1].limit = atol(params[2].c_str());
+ exceptions[nexceptions - 1].who = sstrdup(params[3].c_str());
+ exceptions[nexceptions - 1].time = strtol(params[4].c_str(), NULL, 10);
+ exceptions[nexceptions - 1].expires = strtol(params[5].c_str(), NULL, 10);
+ exceptions[nexceptions - 1].reason = sstrdup(params[6].c_str());
+ exceptions[nexceptions - 1].num = nexceptions - 1;
+ }
+}
+
+void Write(const std::string &buf)
+{
+ db << buf << endl;
+}
+
+void WriteMetadata(const std::string &key, const std::string &data)
+{
+ Write("MD " + key + " " + data);
+}
+
+class DBPlain : public Module
+{
+ /* Day the last backup was on */
+ int LastDay;
+ /* Backup file names */
+ std::list<std::string> Backups;
+ public:
+ DBPlain(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(DATABASE);
+
+ Implementation i[] = { I_OnReload, I_OnDatabaseRead, I_OnLoadDatabase, I_OnDatabaseReadMetadata, I_OnSaveDatabase, I_OnModuleLoad };
+ ModuleManager::Attach(i, this, 6);
+
+ OnReload(true);
+
+ LastDay = 0;
+ }
+
+ ~DBPlain()
+ {
+ }
+
+ void BackupDatabase()
+ {
+ /* Do not backup a database that doesn't exist */
+ if (!IsFile(DatabaseFile))
+ return;
+
+ time_t now = time(NULL);
+ tm *tm = localtime(&now);
+
+ if (tm->tm_mday != LastDay)
+ {
+ LastDay = tm->tm_mday;
+ char *newname = new char[DatabaseFile.length() + 30];
+ snprintf(newname, DatabaseFile.length() + 30, "backups/%s.%d%d%d", DatabaseFile.c_str(), tm->tm_year, tm->tm_mon, tm->tm_mday);
+
+ /* Backup already exists */
+ if (IsFile(newname))
+ {
+ delete [] newname;
+ return;
+ }
+
+ Alog(LOG_DEBUG) << "db_plain: Attemping to rename " << DatabaseFile << " to " << newname;
+ if (rename(DatabaseFile.c_str(), newname) != 0)
+ {
+ ircdproto->SendGlobops(OperServ, "Unable to backup database!");
+ Alog() << "Unable to back up database!";
+
+ if (!Config.NoBackupOkay)
+ quitting = 1;
+
+ delete [] newname;
+
+ return;
+ }
+
+ Backups.push_back(newname);
+
+ delete [] newname;
+
+ unsigned KeepBackups = Config.KeepBackups;
+ if (KeepBackups && Backups.size() > KeepBackups)
+ {
+ DeleteFile(Backups.front().c_str());
+ Backups.pop_front();
+ }
+ }
+ }
+
+ void OnReload(bool)
+ {
+ ConfigReader config;
+ DatabaseFile = config.ReadValue("db_plain", "database", "anope.db", 0);
+ }
+
+ EventReturn OnDatabaseRead(const std::vector<std::string> &params)
+ {
+ std::string key = params[0];
+ std::vector<std::string> otherparams = params;
+ otherparams.erase(otherparams.begin());
+
+ if (key == "NC")
+ LoadNickCore(otherparams);
+ else if (key == "NA")
+ LoadNickAlias(otherparams);
+ else if (key == "NR")
+ LoadNickRequest(otherparams);
+ else if (key == "BI")
+ LoadBotInfo(otherparams);
+ else if (key == "CH")
+ LoadChanInfo(otherparams);
+ else if (key == "OS")
+ LoadOperInfo(otherparams);
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnLoadDatabase()
+ {
+ ReadDatabase();
+
+ /* No need to ever reload this again, although this should never be trigged again */
+ ModuleManager::Detach(I_OnLoadDatabase, this);
+ ModuleManager::Detach(I_OnDatabaseReadMetadata, this);
+
+ return EVENT_STOP;
+ }
+
+ EventReturn OnDatabaseReadMetadata(NickCore *nc, const std::string &key, const std::vector<std::string> &params)
+ {
+ if (key == "EMAIL")
+ nc->email = sstrdup(params[0].c_str());
+ else if (key == "GREET")
+ nc->greet = sstrdup(params[0].c_str());
+ else if (key == "ACCESS")
+ nc->AddAccess(params[0]);
+ else if (key == "FLAGS")
+ {
+ for (unsigned j = 0, end = params.size(); j < end; ++j)
+ for (int i = 0; NickCoreFlags[i].Flag != -1; ++i)
+ if (NickCoreFlags[i].Name == params[j])
+ nc->SetFlag(NickCoreFlags[i].Flag);
+ }
+ else if (key == "MI")
+ {
+ Memo *m = new Memo;
+ m->number = atoi(params[0].c_str());
+ m->time = strtol(params[1].c_str(), NULL, 10);
+ m->sender = params[2];
+ for (unsigned j = 3; params[j] == "UNREAD" || params[j] == "RECEIPT" || params[j] == "NOTIFYS"; ++j)
+ {
+ if (params[j] == "UNREAD")
+ m->SetFlag(MF_UNREAD);
+ else if (params[j] == "RECEIPT")
+ m->SetFlag(MF_RECEIPT);
+ else if (params[j] == "NOTIFYS")
+ m->SetFlag(MF_NOTIFYS);
+ }
+ m->text = sstrdup(params[params.size() - 1].c_str());
+ nc->memos.memos.push_back(m);
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnDatabaseReadMetadata(NickAlias *na, const std::string &key, const std::vector<std::string> &params)
+ {
+ if (key == "LAST_USERMASK")
+ na->last_usermask = sstrdup(params[0].c_str());
+ else if (key == "LAST_REALNAME")
+ na->last_realname = sstrdup(params[0].c_str());
+ else if (key == "LAST_QUIT")
+ na->last_quit = sstrdup(params[0].c_str());
+ else if (key == "FLAGS")
+ {
+ for (unsigned j = 0, end = params.size(); j < end; ++j)
+ {
+ if (params[j] == "FORBIDDEN")
+ na->SetFlag(NS_FORBIDDEN);
+ else if (params[j] == "NOEXPIRE")
+ na->SetFlag(NS_NO_EXPIRE);
+ }
+ }
+ else if (key == "VHOST")
+ na->hostinfo.SetVhost(params.size() > 3 ? params[3] : "", params[2], params[0], strtol(params[1].c_str(), NULL, 10));
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnDatabaseReadMetadata(BotInfo *bi, const std::string &key, const std::vector<std::string> &params)
+ {
+ if (key == "FLAGS")
+ {
+ for (unsigned j = 0, end = params.size(); j < end; ++j)
+ {
+ if (params[j] == "PRIVATE")
+ bi->SetFlag(BI_PRIVATE);
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnDatabaseReadMetadata(ChannelInfo *ci, const std::string &key, const std::vector<std::string> &params)
+ {
+ if (key == "FOUNDER")
+ {
+ ci->founder = findcore(params[0]);
+ if (!ci->founder)
+ {
+ std::stringstream reason;
+ reason << "Deleting founderless channel " << ci->name << " (founder: " << params[0] << ")";
+ throw DatabaseException(reason.str());
+ }
+ }
+ else if (key == "SUCCESSOR")
+ ci->successor = findcore(params[0]);
+ else if (key == "LEVELS")
+ {
+ for (unsigned j = 0, end = params.size(); j < end; j += 2)
+ for (int i = 0; ChannelLevels[i].Level != -1; ++i)
+ if (ChannelLevels[i].Name == params[j])
+ ci->levels[ChannelLevels[i].Level] = atoi(params[j + 1].c_str());
+ }
+ else if (key == "FLAGS")
+ {
+ for (unsigned j = 0, end = params.size(); j < end; ++j)
+ for (int i = 0; ChannelInfoFlags[i].Flag != -1; ++i)
+ if (ChannelInfoFlags[i].Name == params[j])
+ ci->SetFlag(ChannelInfoFlags[i].Flag);
+ }
+ else if (key == "DESC")
+ ci->desc = sstrdup(params[0].c_str());
+ else if (key == "TOPIC")
+ {
+ ci->last_topic_setter = params[0];
+ ci->last_topic_time = strtol(params[1].c_str(), NULL, 10);
+ ci->last_topic = sstrdup(params[2].c_str());
+ }
+ else if (key == "FORBID")
+ {
+ ci->forbidby = sstrdup(params[0].c_str());
+ ci->forbidreason = sstrdup(params[1].c_str());
+ }
+ else if (key == "ACCESS")
+ {
+ NickCore *nc = findcore(params[0]);
+ if (!nc)
+ {
+ std::stringstream reason;
+ reason << "Access entry for nonexistant core " << params[0] << " on " << ci->name;
+ throw DatabaseException(reason.str());
+ }
+
+ int level = atoi(params[1].c_str());
+ time_t last_seen = strtol(params[2].c_str(), NULL, 10);
+ ci->AddAccess(nc, level, params[3], last_seen);
+ }
+ else if (key == "AKICK")
+ {
+ bool Stuck = params[0] == "STUCK" ? true : false;
+ bool Nick = params[1] == "NICK" ? true : false;
+ NickCore *nc = NULL;
+ if (Nick)
+ {
+ nc = findcore(params[2]);
+ if (!nc)
+ {
+ std::stringstream reason;
+ reason << "Akick for nonexistant core " << params[2] << " on " << ci->name;
+ throw DatabaseException(reason.str());
+ }
+ }
+ AutoKick *ak;
+ if (Nick)
+ ak = ci->AddAkick(params[3], nc, params.size() > 6 ? params[6] : "", atol(params[4].c_str()), atol(params[5].c_str()));
+ else
+ ak = ci->AddAkick(params[3], params[2], params.size() > 6 ? params[6] : "", atol(params[4].c_str()), atol(params[5].c_str()));
+ if (Stuck)
+ ak->SetFlag(AK_STUCK);
+ if (Nick)
+ ak->SetFlag(AK_ISNICK);
+
+ }
+ else if (key == "MLOCK_ON" || key == "MLOCK_OFF")
+ {
+ bool Set = key == "MLOCK_ON" ? true : false;
+
+ /* For now store mlock in extensible, Anope hasn't yet connected to the IRCd and doesn't know what modes exist */
+ ci->Extend(Set ? "db_mlock_modes_on" : "db_mlock_modes_off", new ExtensibleItemRegular<std::vector<std::string> >(params));
+ }
+ else if (key == "MLP")
+ {
+ std::vector<std::pair<std::string, std::string> > mlp;
+
+ for (unsigned j = 0, end = params.size(); j < end; j += 2)
+ mlp.push_back(std::make_pair(params[j], params[j + 1]));
+
+ /* For now store mlocked modes in extensible, Anope hasn't yet connected to the IRCd and doesn't know what modes exist */
+ ci->Extend("db_mlp", new ExtensibleItemRegular<std::vector<std::pair<std::string, std::string> > >(mlp));
+ }
+ else if (key == "MI")
+ {
+ Memo *m = new Memo;
+ m->number = atoi(params[0].c_str());
+ m->time = strtol(params[1].c_str(), NULL, 10);
+ m->sender = params[2];
+ for (unsigned j = 3; params[j] == "UNREAD" || params[j] == "RECEIPT" || params[j] == "NOTIFYS"; ++j)
+ {
+ if (params[j] == "UNREAD")
+ m->SetFlag(MF_UNREAD);
+ else if (params[j] == "RECEIPT")
+ m->SetFlag(MF_RECEIPT);
+ else if (params[j] == "NOTIFYS")
+ m->SetFlag(MF_NOTIFYS);
+ }
+ m->text = sstrdup(params[params.size() - 1].c_str());
+ ci->memos.memos.push_back(m);
+ }
+ else if (key == "ENTRYMSG")
+ ci->entry_message = sstrdup(params[0].c_str());
+ else if (key == "BI")
+ {
+ if (params[0] == "NAME")
+ ci->bi = findbot(params[1]);
+ else if (params[0] == "FLAGS")
+ {
+ for (unsigned j = 1, end = params.size(); j < end; ++j)
+ for (int i = 0; BotFlags[i].Flag != -1; ++i)
+ if (BotFlags[i].Name == params[j])
+ ci->botflags.SetFlag(BotFlags[i].Flag);
+ }
+ else if (params[0] == "TTB")
+ {
+ for (unsigned j = 1, end = params.size(); j < end; j += 2)
+ {
+ if (params[j] == "BOLDS")
+ ci->ttb[0] = atoi(params[j + 1].c_str());
+ else if (params[j] == "COLORS")
+ ci->ttb[1] = atoi(params[j + 1].c_str());
+ else if (params[j] == "REVERSES")
+ ci->ttb[2] = atoi(params[j + 1].c_str());
+ else if (params[j] == "UNDERLINES")
+ ci->ttb[3] = atoi(params[j + 1].c_str());
+ else if (params[j] == "BADWORDS")
+ ci->ttb[4] = atoi(params[j + 1].c_str());
+ else if (params[j] == "CAPS")
+ ci->ttb[5] = atoi(params[j + 1].c_str());
+ else if (params[j] == "FLOOD")
+ ci->ttb[6] = atoi(params[j + 1].c_str());
+ else if (params[j] == "REPEAT")
+ ci->ttb[7] = atoi(params[j + 1].c_str());
+ }
+ }
+ else if (params[0] == "CAPSMIN")
+ ci->capsmin = atoi(params[1].c_str());
+ else if (params[0] == "CAPSPERCENT")
+ ci->capspercent = atoi(params[1].c_str());
+ else if (params[0] == "FLOODLINES")
+ ci->floodlines = atoi(params[1].c_str());
+ else if (params[0] == "FLOODSECS")
+ ci->floodsecs = atoi(params[1].c_str());
+ else if (params[0] == "REPEATTIMES")
+ ci->repeattimes = atoi(params[1].c_str());
+ else if (params[0] == "BADWORD")
+ {
+ BadWordType Type;
+ if (params[1] == "SINGLE")
+ Type = BW_SINGLE;
+ else if (params[1] == "START")
+ Type = BW_START;
+ else if (params[1] == "END")
+ Type = BW_END;
+ else
+ Type = BW_ANY;
+ ci->AddBadWord(params[2], Type);
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnSaveDatabase()
+ {
+ BackupDatabase();
+
+ db.clear();
+ db.open(DatabaseFile.c_str(), std::ios_base::out | std::ios_base::trunc);
+
+ if (!db.is_open())
+ {
+ ircdproto->SendGlobops(NULL, "Unable to open %s for writing!", DatabaseFile.c_str());
+ return EVENT_CONTINUE;
+ }
+
+ db << "VER 1" << endl;
+
+ for (nickrequest_map::const_iterator it = NickRequestList.begin(), it_end = NickRequestList.end(); it != it_end; ++it)
+ {
+ NickRequest *nr = it->second;
+
+ db << "NR " << nr->nick << " " << nr->passcode << " " << nr->password << " " << nr->email << " " << nr->requested << endl;
+
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, nr));
+ }
+
+ for (nickcore_map::const_iterator nit = NickCoreList.begin(), nit_end = NickCoreList.end(); nit != nit_end; ++nit)
+ {
+ NickCore *nc = nit->second;
+
+ if (nc->HasFlag(NI_FORBIDDEN))
+ {
+ db << "NC " << nc->display << endl;
+ db << "MD FLAGS FORBIDDEN" << endl;
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, nc));
+ continue;
+ }
+ else
+ db << "NC " << nc->display << " " << nc->pass << " ";
+ for (int j = 0; LangInfos[j].LanguageId != -1; ++j)
+ if (nc->language == LangInfos[j].LanguageId)
+ db << LangInfos[j].Name;
+ db << " " << nc->memos.memomax << " " << nc->channelcount << endl;
+
+ if (nc->email)
+ db << "MD EMAIL " << nc->email << endl;
+ if (nc->greet)
+ db << "MD GREET :" << nc->greet << endl;
+ if (!nc->access.empty())
+ {
+ for (std::vector<std::string>::iterator it = nc->access.begin(), it_end = nc->access.end(); it != it_end; ++it)
+ db << "MD ACCESS " << *it << endl;
+ }
+ if (nc->FlagCount())
+ {
+ db << "MD FLAGS";
+ for (int j = 0; NickCoreFlags[j].Flag != -1; ++j)
+ if (nc->HasFlag(NickCoreFlags[j].Flag))
+ db << " " << NickCoreFlags[j].Name;
+ db << endl;
+ }
+ if (!nc->memos.memos.empty())
+ {
+ MemoInfo *mi = &nc->memos;
+ for (unsigned k = 0, end = mi->memos.size(); k < end; ++k)
+ {
+ db << "MD MI " << mi->memos[k]->number << " " << mi->memos[k]->time << " " << mi->memos[k]->sender;
+ if (mi->memos[k]->HasFlag(MF_UNREAD))
+ db << " UNREAD";
+ if (mi->memos[k]->HasFlag(MF_RECEIPT))
+ db << " RECEIPT";
+ if (mi->memos[k]->HasFlag(MF_NOTIFYS))
+ db << " NOTIFYS";
+ db << " :" << mi->memos[k]->text << endl;
+ }
+ }
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, nc));
+ }
+
+ for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it)
+ {
+ NickAlias *na = it->second;
+
+ db << "NA " << na->nc->display << " " << na->nick << " " << na->time_registered << " " << na->last_seen << endl;
+ if (na->last_usermask)
+ db << "MD LAST_USERMASK " << na->last_usermask << endl;
+ if (na->last_realname)
+ db << "MD LAST_REALNAME :" << na->last_realname << endl;
+ if (na->last_quit)
+ db << "MD LAST_QUIT :" << na->last_quit << endl;
+ if (na->HasFlag(NS_FORBIDDEN) || na->HasFlag(NS_NO_EXPIRE))
+ db << "MD FLAGS" << (na->HasFlag(NS_FORBIDDEN) ? " FORBIDDEN" : "") << (na->HasFlag(NS_NO_EXPIRE) ? " NOEXPIRE " : "") << endl;
+ if (na->hostinfo.HasVhost())
+ db << "MD VHOST " << na->hostinfo.GetCreator() << " " << na->hostinfo.GetTime() << " " << na->hostinfo.GetHost() << " :" << na->hostinfo.GetIdent() << endl;
+
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, na));
+ }
+
+ for (botinfo_map::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
+ {
+ BotInfo *bi = it->second;
+
+ db << "BI " << bi->nick << " " << bi->GetIdent() << " " << bi->host << " " << bi->created << " " << bi->chans.size() << " :" << bi->realname << endl;
+ if (bi->FlagCount())
+ {
+ db << "MD FLAGS";
+ if (bi->HasFlag(BI_PRIVATE))
+ db << " PRIVATE";
+ db << endl;
+ }
+ }
+
+ for (registered_channel_map::const_iterator cit = RegisteredChannelList.begin(), cit_end = RegisteredChannelList.end(); cit != cit_end; ++cit)
+ {
+ ChannelInfo *ci = cit->second;
+
+ db << "CH " << ci->name << " " << ci->time_registered << " " << ci->last_used;
+ db << " " << ci->bantype << " " << ci->memos.memomax << endl;
+ if (ci->founder)
+ db << "MD FOUNDER " << ci->founder->display << endl;
+ if (ci->successor)
+ db << "MD SUCCESSOR " << ci->successor->display << endl;
+ if (ci->desc)
+ db << "MD DESC :" << ci->desc << endl;
+ if (ci->last_topic)
+ db << "MD TOPIC " << ci->last_topic_setter << " " << ci->last_topic_time << " :" << ci->last_topic << endl;
+ db << "MD LEVELS";
+ for (int j = 0; ChannelLevels[j].Level != -1; ++j)
+ db << " " << ChannelLevels[j].Name << " " << ci->levels[ChannelLevels[j].Level];
+ db << endl;
+ if (ci->FlagCount())
+ {
+ db << "MD FLAGS";
+ for (int j = 0; ChannelInfoFlags[j].Flag != -1; ++j)
+ if (ci->HasFlag(ChannelInfoFlags[j].Flag))
+ db << " " << ChannelInfoFlags[j].Name;
+ db << endl;
+ if (ci->HasFlag(CI_FORBIDDEN))
+ db << "MD FORBID " << ci->forbidby << " :" << ci->forbidreason << endl;
+ }
+ for (unsigned k = 0, end = ci->GetAccessCount(); k < end; ++k)
+ db << "MD ACCESS " << ci->GetAccess(k)->nc->display << " " << ci->GetAccess(k)->level << " " << ci->GetAccess(k)->last_seen << " " << ci->GetAccess(k)->creator << endl;
+ for (unsigned k = 0, end = ci->GetAkickCount(); k < end; ++k)
+ {
+ db << "MD AKICK " << (ci->GetAkick(k)->HasFlag(AK_STUCK) ? "STUCK " : "UNSTUCK ") << (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? "NICK " : "MASK ") << (ci->GetAkick(k)->HasFlag(AK_ISNICK) ? ci->GetAkick(k)->nc->display : ci->GetAkick(k)->mask) << " " << ci->GetAkick(k)->creator << " " << ci->GetAkick(k)->addtime << " " << ci->last_used << " :";
+ if (!ci->GetAkick(k)->reason.empty())
+ db << ci->GetAkick(k)->reason;
+ db << endl;
+ }
+ if (ci->GetMLockCount(true))
+ {
+ db << "MD MLOCK_ON";
+ for (std::list<Mode *>::iterator it = ModeManager::Modes.begin(), it_end = ModeManager::Modes.end(); it != it_end; ++it)
+ {
+ if ((*it)->Class == MC_CHANNEL)
+ {
+ ChannelMode *cm = dynamic_cast<ChannelMode *>(*it);
+
+ if (ci->HasMLock(cm->Name, true))
+ db << " " << cm->NameAsString;
+ }
+ }
+ db << endl;
+ }
+ if (ci->GetMLockCount(false))
+ {
+ db << "MD MLOCK_OFF";
+ for (std::list<Mode *>::iterator it = ModeManager::Modes.begin(), it_end = ModeManager::Modes.end(); it != it_end; ++it)
+ {
+ if ((*it)->Class == MC_CHANNEL)
+ {
+ ChannelMode *cm = dynamic_cast<ChannelMode *>(*it);
+
+ if (ci->HasMLock(cm->Name, false))
+ db << " " << cm->NameAsString;
+ }
+ }
+ db << endl;
+ }
+ std::string Param;
+ for (std::list<Mode *>::iterator it = ModeManager::Modes.begin(), it_end = ModeManager::Modes.end(); it != it_end; ++it)
+ {
+ if ((*it)->Class == MC_CHANNEL)
+ {
+ ChannelMode *cm = dynamic_cast<ChannelMode *>(*it);
+
+ if (ci->GetParam(cm->Name, Param))
+ db << "MD MLP " << cm->NameAsString << " " << Param << endl;
+ }
+ }
+ if (!ci->memos.memos.empty())
+ {
+ MemoInfo *memos = &ci->memos;
+
+ for (unsigned k = 0, end = memos->memos.size(); k < end; ++k)
+ {
+ db << "MD MI " << memos->memos[k]->number << " " << memos->memos[k]->time << " " << memos->memos[k]->sender;
+ if (memos->memos[k]->HasFlag(MF_UNREAD))
+ db << " UNREAD";
+ if (memos->memos[k]->HasFlag(MF_RECEIPT))
+ db << " RECEIPT";
+ if (memos->memos[k]->HasFlag(MF_NOTIFYS))
+ db << " NOTIFYS";
+ db << " :" << memos->memos[k]->text << endl;
+ }
+ }
+ if (ci->entry_message)
+ db << "MD ENTRYMSG :" << ci->entry_message << endl;
+ if (ci->bi)
+ db << "MD BI NAME " << ci->bi->nick << endl;
+ if (ci->botflags.FlagCount())
+ {
+ db << "MD BI FLAGS";
+ for (int j = 0; BotFlags[j].Flag != -1; ++j)
+ if (ci->botflags.HasFlag(BotFlags[j].Flag))
+ db << " " << BotFlags[j].Name;
+ db << endl;
+ }
+ db << "MD BI TTB BOLDS " << ci->ttb[0] << " COLORS " << ci->ttb[1] << " REVERSES " << ci->ttb[2] << " UNDERLINES " << ci->ttb[3] << " BADWORDS " << ci->ttb[4] << " CAPS " << ci->ttb[5] << " FLOOD " << ci->ttb[6] << " REPEAT " << ci->ttb[7] << endl;
+ if (ci->capsmin)
+ db << "MD BI CAPSMIN " << ci->capsmin << endl;
+ if (ci->capspercent)
+ db << "MD BI CAPSPERCENT " << ci->capspercent << endl;
+ if (ci->floodlines)
+ db << "MD BI FLOODLINES " << ci->floodlines << endl;
+ if (ci->floodsecs)
+ db << "MD BI FLOODSECS " << ci->floodsecs << endl;
+ if (ci->repeattimes)
+ db << "MD BI REPEATTIMES " << ci->repeattimes << endl;
+ for (unsigned k = 0, end = ci->GetBadWordCount(); k < end; ++k)
+ db << "MD BI BADWORD " << (ci->GetBadWord(k)->type == BW_ANY ? "ANY " : "") << (ci->GetBadWord(k)->type == BW_SINGLE ? "SINGLE " : "") << (ci->GetBadWord(k)->type == BW_START ? "START " : "") << (ci->GetBadWord(k)->type == BW_END ? "END " : "") << ":" << ci->GetBadWord(k)->word << endl;
+
+ FOREACH_MOD(I_OnDatabaseWriteMetadata, OnDatabaseWriteMetadata(WriteMetadata, ci));
+ }
+
+ db << "OS STATS " << maxusercnt << " " << maxusertime << endl;
+
+ if (SGLine)
+ for (unsigned i = 0, end = SGLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SGLine->GetEntry(i);
+ db << "OS AKILL " << x->GetUser() << " " << x->GetHost() << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl;
+ }
+
+ if (SNLine)
+ for (unsigned i = 0, end = SNLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SNLine->GetEntry(i);
+ db << "OS SNLINE " << x->Mask << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl;
+ }
+
+ if (SQLine)
+ for (unsigned i = 0, end = SQLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SQLine->GetEntry(i);
+ db << "OS SQLINE " << x->Mask << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl;
+ }
+
+ if (SZLine)
+ for (unsigned i = 0, end = SZLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SZLine->GetEntry(i);
+ db << "OS SZLINE " << x->Mask << " " << x->By << " " << x->Created << " " << x->Expires << " :" << x->Reason << endl;
+ }
+
+ for (int i = 0; i < nexceptions; ++i)
+ db << "OS EXCEPTION " << exceptions[i].mask << " " << exceptions[i].limit << " " << exceptions[i].who << " " << exceptions[i].time << " " << exceptions[i].expires << " " << exceptions[i].reason << endl;
+
+ FOREACH_MOD(I_OnDatabaseWrite, OnDatabaseWrite(Write));
+
+ db.close();
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnModuleLoad(User *u, Module *m)
+ {
+ if (!u)
+ return;
+
+ Implementation events[] = {I_OnDatabaseRead, I_OnDatabaseReadMetadata};
+ for (int i = 0; i < 2; ++i)
+ {
+ std::vector<Module *>::iterator it = std::find(ModuleManager::EventHandlers[events[i]].begin(), ModuleManager::EventHandlers[events[i]].end(), m);
+ /* This module wants to read from the database */
+ if (it != ModuleManager::EventHandlers[events[i]].end())
+ /* Loop over the database and call the events it would on a normal startup */
+ ReadDatabase(m);
+ }
+ }
+};
+
+MODULE_INIT(DBPlain)
diff --git a/modules/core/enc_md5.cpp b/modules/core/enc_md5.cpp
new file mode 100644
index 000000000..fafb79309
--- /dev/null
+++ b/modules/core/enc_md5.cpp
@@ -0,0 +1,373 @@
+/* Module for encryption using MD5.
+ *
+ * Modified for Anope.
+ * (C) 2003-2010 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Taken from IRC Services and is copyright (c) 1996-2002 Andrew Church.
+ * E-mail: <achurch@achurch.org>
+ * Parts written by Andrew Kempe and others.
+ * This program is free but copyrighted software; see the file COPYING for
+ * details.
+ */
+
+#include "module.h"
+
+/*************************************************************************/
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* MD5 context. */
+struct MD5_CTX
+{
+ unsigned state[4]; /* state (ABCD) */
+ unsigned count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+};
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Constants for MD5Transform routine.
+ */
+enum
+{
+ S11 = 7,
+ S12 = 12,
+ S13 = 17,
+ S14 = 22,
+ S21 = 5,
+ S22 = 9,
+ S23 = 14,
+ S24 = 20,
+ S31 = 4,
+ S32 = 11,
+ S33 = 16,
+ S34 = 23,
+ S41 = 6,
+ S42 = 10,
+ S43 = 15,
+ S44 = 21
+};
+
+void MD5Transform(unsigned [4], const unsigned char [64]);
+void Encode(unsigned char *, unsigned *, unsigned);
+void Decode(unsigned *, const unsigned char *, unsigned);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+inline static unsigned F(unsigned x, unsigned y, unsigned z) { return (x & y) | (~x & z); }
+inline static unsigned G(unsigned x, unsigned y, unsigned z) { return (x & z) | (y & ~z); }
+inline static unsigned H(unsigned x, unsigned y, unsigned z) { return x ^ y ^ z; }
+inline static unsigned I(unsigned x, unsigned y, unsigned z) { return y ^ (x | ~z); }
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+inline static unsigned ROTATE_LEFT(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); }
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+inline static void FF(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += F(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+inline static void GG(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += G(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+inline static void HH(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += H(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+inline static void II(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += I(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void MD5Init(MD5_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants. */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating the
+ * context.
+ */
+void MD5Update(MD5_CTX *context, const unsigned char *input, unsigned inputLen)
+{
+ unsigned i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (context->count[0] >> 3) & 0x3F;
+
+ /* Update number of bits */
+ if ((context->count[0] += inputLen << 3) < (inputLen << 3))
+ ++context->count[1];
+ context->count[1] += inputLen >> 29;
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen)
+ {
+ memcpy(&context->buffer[index], input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy(&context->buffer[index], &input[i], inputLen - i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+void MD5Final(unsigned char digest[16], MD5_CTX *context)
+{
+ unsigned char bits[8];
+ unsigned index, padLen;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8);
+
+ /* Pad out to 56 mod 64. */
+ index = (context->count[0] >> 3) & 0x3f;
+ padLen = index < 56 ? 56 - index : 120 - index;
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+ /* Store state in digest */
+ Encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information. */
+ memset(context, 0, sizeof(*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+void MD5Transform(unsigned state[4], const unsigned char block[64])
+{
+ unsigned a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ memset(x, 0, sizeof(x));
+}
+
+/* Encodes input (unsigned) into output (unsigned char). Assumes len is
+ * a multiple of 4.
+ */
+void Encode(unsigned char *output, unsigned *input, unsigned len)
+{
+ unsigned i, j;
+
+ for (i = 0, j = 0; j < len; ++i, j += 4)
+ {
+ output[j] = static_cast<unsigned char>(input[i] & 0xff);
+ output[j + 1] = static_cast<unsigned char>((input[i] >> 8) & 0xff);
+ output[j + 2] = static_cast<unsigned char>((input[i] >> 16) & 0xff);
+ output[j + 3] = static_cast<unsigned char>((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (unsigned). Assumes len is
+ * a multiple of 4.
+ */
+void Decode(unsigned *output, const unsigned char *input, unsigned len)
+{
+ unsigned i, j;
+
+ for (i = 0, j = 0; j < len; ++i, j += 4)
+ output[i] = static_cast<unsigned>(input[j]) | (static_cast<unsigned>(input[j + 1]) << 8) | (static_cast<unsigned>(input[j + 2]) << 16) | (static_cast<unsigned>(input[j + 3]) << 24);
+}
+
+/*************************************************************************/
+
+/* Module stuff. */
+
+class EMD5 : public Module
+{
+ public:
+ EMD5(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(ENCRYPTION);
+
+ ModuleManager::Attach(I_OnEncrypt, this);
+ ModuleManager::Attach(I_OnDecrypt, this);
+ ModuleManager::Attach(I_OnCheckPassword, this);
+ }
+
+ EventReturn OnEncrypt(const std::string &src, std::string &dest)
+ {
+ MD5_CTX context;
+ char *digest = new char[Config.PassLen];
+ std::string buf = "md5:";
+ char cpass[1000];
+
+ MD5Init(&context);
+ MD5Update(&context, reinterpret_cast<const unsigned char *>(src.c_str()), src.size());
+ MD5Final(reinterpret_cast<unsigned char *>(digest), &context);
+
+ b64_encode(digest, 16, cpass, 1000);
+ buf += cpass;
+ Alog(LOG_DEBUG_2) << "(enc_md5) hashed password from [" << src << "] to [" << buf << "]";
+ dest = buf;
+ delete [] digest;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnDecrypt(const std::string &hashm, const std::string &src, std::string &dest)
+ {
+ if (hashm != "md5")
+ return EVENT_CONTINUE;
+ return EVENT_STOP;
+ }
+
+ EventReturn OnCheckPassword(const std::string &hashm, std::string &plaintext, std::string &password)
+ {
+ if (hashm != "md5")
+ return EVENT_CONTINUE;
+ std::string buf;
+ this->OnEncrypt(plaintext, buf);
+ if (password == buf)
+ {
+ /* if we are NOT the first module in the list,
+ * we want to re-encrypt the pass with the new encryption
+ */
+ if (Config.EncModuleList.front() != this->name)
+ enc_encrypt(plaintext, password);
+ return EVENT_ALLOW;
+ }
+ return EVENT_STOP;
+ }
+};
+
+MODULE_INIT(EMD5)
diff --git a/modules/core/enc_none.cpp b/modules/core/enc_none.cpp
new file mode 100644
index 000000000..58ccdaff1
--- /dev/null
+++ b/modules/core/enc_none.cpp
@@ -0,0 +1,67 @@
+/* Module for plain text encryption.
+ *
+ * (C) 2003-2010 Anope Team
+ * Contact us at team@anope.org
+ *
+ * This program is free but copyrighted software; see the file COPYING for
+ * details.
+ */
+
+#include "module.h"
+
+class ENone : public Module
+{
+ public:
+ ENone(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(ENCRYPTION);
+
+ ModuleManager::Attach(I_OnEncrypt, this);
+ ModuleManager::Attach(I_OnDecrypt, this);
+ ModuleManager::Attach(I_OnCheckPassword, this);
+ }
+
+ EventReturn OnEncrypt(const std::string &src, std::string &dest)
+ {
+ std::string buf = "plain:";
+ char cpass[1000];
+ b64_encode(src.c_str(), src.size(), cpass, 1000);
+ buf += cpass;
+ Alog(LOG_DEBUG_2) << "(enc_none) hashed password from [" << src << "] to [" << buf << "]";
+ dest = buf;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnDecrypt(const std::string &hashm, const std::string &src, std::string &dest)
+ {
+ if (hashm != "plain")
+ return EVENT_CONTINUE;
+ char cpass[1000] = "";
+ size_t pos = src.find(":");
+ std::string buf(src.begin() + pos + 1, src.end());
+ b64_decode(buf.c_str(), cpass, 1000);
+ dest = cpass;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnCheckPassword(const std::string &hashm, std::string &plaintext, std::string &password)
+ {
+ if (hashm != "plain")
+ return EVENT_CONTINUE;
+ std::string buf;
+ this->OnEncrypt(plaintext, buf);
+ if (password == buf)
+ {
+ /* if we are NOT the first module in the list,
+ * we want to re-encrypt the pass with the new encryption
+ */
+ if (Config.EncModuleList.front() != this->name)
+ enc_encrypt(plaintext, password);
+ return EVENT_ALLOW;
+ }
+ return EVENT_STOP;
+ }
+};
+
+MODULE_INIT(ENone)
diff --git a/modules/core/enc_old.cpp b/modules/core/enc_old.cpp
new file mode 100644
index 000000000..a53115628
--- /dev/null
+++ b/modules/core/enc_old.cpp
@@ -0,0 +1,381 @@
+/* Include file for high-level encryption routines.
+ *
+ * (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"
+
+/******** Code specific to the type of encryption. ********/
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* MD5 context. */
+struct MD5_CTX
+{
+ unsigned state[4]; /* state (ABCD) */
+ unsigned count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+};
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Constants for MD5Transform routine.
+ */
+enum
+{
+ S11 = 7,
+ S12 = 12,
+ S13 = 17,
+ S14 = 22,
+ S21 = 5,
+ S22 = 9,
+ S23 = 14,
+ S24 = 20,
+ S31 = 4,
+ S32 = 11,
+ S33 = 16,
+ S34 = 23,
+ S41 = 6,
+ S42 = 10,
+ S43 = 15,
+ S44 = 21
+};
+
+static void MD5Transform(unsigned [4], const unsigned char[64]);
+static void Encode(unsigned char *, unsigned *, unsigned);
+static void Decode(unsigned *, const unsigned char *, unsigned);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+inline static unsigned F(unsigned x, unsigned y, unsigned z) { return (x & y) | (~x & z); }
+inline static unsigned G(unsigned x, unsigned y, unsigned z) { return (x & z) | (y & ~z); }
+inline static unsigned H(unsigned x, unsigned y, unsigned z) { return x ^ y ^ z; }
+inline static unsigned MD5_I(unsigned x, unsigned y, unsigned z) { return y ^ (x | ~z); }
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+inline static unsigned ROTATE_LEFT(unsigned x, unsigned n) { return (x << n) | (x >> (32 - n)); }
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+inline static void FF(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += F(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+inline static void GG(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += G(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+inline static void HH(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += H(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+inline static void II(unsigned &a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned s, unsigned ac)
+{
+ a += MD5_I(b, c, d) + x + ac;
+ a = ROTATE_LEFT(a, s);
+ a += b;
+}
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+static void MD5Init(MD5_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating the
+ * context.
+ */
+static void MD5Update(MD5_CTX *context, const unsigned char *input, unsigned inputLen)
+{
+ unsigned i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (context->count[0] >> 3) & 0x3F;
+
+ /* Update number of bits */
+ if ((context->count[0] += inputLen << 3) < (inputLen << 3))
+ ++context->count[1];
+ context->count[1] += inputLen >> 29;
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen)
+ {
+ memcpy(&context->buffer[index], input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy(&context->buffer[index], &input[i], inputLen - i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ * the message digest and zeroizing the context.
+ */
+static void MD5Final(unsigned char digest[16], MD5_CTX *context)
+{
+ unsigned char bits[8];
+ unsigned index, padLen;
+
+ /* Save number of bits */
+ Encode(bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (context->count[0] >> 3) & 0x3f;
+ padLen = index < 56 ? 56 - index : 120 - index;
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+ /* Store state in digest */
+ Encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+ */
+ memset(context, 0, sizeof(*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform(unsigned state[4], const unsigned char block[64])
+{
+ unsigned a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode(x, block, 64);
+
+ /* Round 1 */
+ FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
+ FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
+ FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
+ FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
+ FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
+ FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
+ FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
+ FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
+ FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
+ FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
+ FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
+ GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
+ GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
+ GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
+ GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
+ GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
+ GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
+ GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
+ GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
+ GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
+ GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
+ HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
+ HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
+ HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
+ HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
+ HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
+ HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
+ HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
+ HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
+ HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
+ II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
+ II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
+ II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
+ II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
+ II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
+ II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
+ II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
+ II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
+ II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+ */
+ memset(x, 0, sizeof(x));
+}
+
+/* Encodes input (unsigned) into output (unsigned char). Assumes len is
+ * a multiple of 4.
+ */
+static void Encode(unsigned char *output, unsigned *input, unsigned len)
+{
+ unsigned i, j;
+
+ for (i = 0, j = 0; j < len; ++i, j += 4)
+ {
+ output[j] = static_cast<unsigned char>(input[i] & 0xff);
+ output[j + 1] = static_cast<unsigned char>((input[i] >> 8) & 0xff);
+ output[j + 2] = static_cast<unsigned char>((input[i] >> 16) & 0xff);
+ output[j + 3] = static_cast<unsigned char>((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (unsigned). Assumes len is
+ * a multiple of 4.
+ */
+static void Decode(unsigned *output, const unsigned char *input, unsigned len)
+{
+ unsigned i, j;
+
+ for (i = 0, j = 0; j < len; ++i, j += 4)
+ output[i] = static_cast<unsigned>(input[j]) | (static_cast<unsigned>(input[j + 1]) << 8) | (static_cast<unsigned>(input[j + 2]) << 16) | (static_cast<unsigned>(input[j + 3]) << 24);
+}
+
+/*************************************************************************/
+
+/******** Our own high-level routines. ********/
+
+inline static char XTOI(char c) { return c > 9 ? c - 'A' + 10 : c - '0'; }
+
+class EOld : public Module
+{
+ public:
+ EOld(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(ENCRYPTION);
+
+ ModuleManager::Attach(I_OnEncrypt, this);
+ ModuleManager::Attach(I_OnDecrypt, this);
+ ModuleManager::Attach(I_OnCheckPassword, this);
+ }
+
+ EventReturn OnEncrypt(const std::string &src, std::string &dest)
+ {
+ MD5_CTX context;
+ char digest[33] = "", digest2[33], cpass[1000];
+ int i;
+ std::string buf = "oldmd5:";
+
+ memset(&context, 0, sizeof(context));
+
+ MD5Init(&context);
+ MD5Update(&context, reinterpret_cast<const unsigned char *>(src.c_str()), src.size());
+ MD5Final(reinterpret_cast<unsigned char *>(digest), &context);
+ for (i = 0; i < 32; i += 2)
+ digest2[i / 2] = XTOI(digest[i]) << 4 | XTOI(digest[i + 1]);
+
+ b64_encode(digest2, 16, cpass, 1000);
+ buf += cpass;
+ Alog(LOG_DEBUG_2) << "(enc_old) hashed password from [" << src << "] to [" << buf << "]";
+ dest = buf;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnDecrypt(const std::string &hashm, const std::string &src, std::string &dest )
+ {
+ if (hashm != "oldmd5")
+ return EVENT_CONTINUE;
+ return EVENT_STOP;
+ }
+
+ EventReturn OnCheckPassword(const std::string &hashm, std::string &plaintext, std::string &password)
+ {
+ if (hashm != "oldmd5")
+ return EVENT_CONTINUE;
+ std::string buf;
+ this->OnEncrypt(plaintext, buf);
+ if (password == buf)
+ {
+ /* if we are NOT the first module in the list,
+ * we want to re-encrypt the pass with the new encryption
+ */
+ if (Config.EncModuleList.front() != this->name)
+ enc_encrypt(plaintext, password);
+ return EVENT_ALLOW;
+ }
+ return EVENT_STOP;
+ }
+};
+
+MODULE_INIT(EOld)
diff --git a/modules/core/enc_sha1.cpp b/modules/core/enc_sha1.cpp
new file mode 100644
index 000000000..b986d6938
--- /dev/null
+++ b/modules/core/enc_sha1.cpp
@@ -0,0 +1,229 @@
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd if true. */
+
+#include "module.h"
+
+struct SHA1_CTX
+{
+ uint32 state[5];
+ uint32 count[2];
+ unsigned char buffer[64];
+};
+
+void SHA1Transform(uint32 state[5], const unsigned char buffer[64]);
+void SHA1Init(SHA1_CTX *context);
+void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32 len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX *context);
+
+inline static uint32 rol(uint32 value, uint32 bits) { return (value << bits) | (value >> (32 - bits)); }
+
+union CHAR64LONG16
+{
+ unsigned char c[64];
+ uint32 l[16];
+};
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+inline static uint32 blk0(CHAR64LONG16 *block, uint32 i)
+{
+#ifdef LITTLE_ENDIAN
+ return block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | (rol(block->l[i], 8) & 0x00FF00FF);
+#else
+ return block->l[i];
+#endif
+}
+inline static uint32 blk(CHAR64LONG16 *block, uint32 i) { return block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15],1); }
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+inline static void R0(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); }
+inline static void R1(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + rol(v, 5); w = rol(w, 30); }
+inline static void R2(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); }
+inline static void R3(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + rol(v, 5); w = rol(w, 30); }
+inline static void R4(CHAR64LONG16 *block, uint32 v, uint32 &w, uint32 x, uint32 y, uint32 &z, uint32 i) { z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30); }
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(uint32 state[5], const unsigned char buffer[64])
+{
+ uint32 a, b, c, d, e;
+ static unsigned char workspace[64];
+ CHAR64LONG16 *block = reinterpret_cast<CHAR64LONG16 *>(workspace);
+ memcpy(block, buffer, 64);
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(block, a, b, c, d, e, 0); R0(block, e, a, b, c, d, 1); R0(block, d, e, a, b, c, 2); R0(block, c, d, e, a, b, 3);
+ R0(block, b, c, d, e, a, 4); R0(block, a, b, c, d, e, 5); R0(block, e, a, b, c, d, 6); R0(block, d, e, a, b, c, 7);
+ R0(block, c, d, e, a, b, 8); R0(block, b, c, d, e, a, 9); R0(block, a, b, c, d, e, 10); R0(block, e, a, b, c, d, 11);
+ R0(block, d, e, a, b, c, 12); R0(block, c, d, e, a, b, 13); R0(block, b, c, d, e, a, 14); R0(block, a, b, c, d, e, 15);
+ R1(block, e, a, b, c, d, 16); R1(block, d, e, a, b, c, 17); R1(block, c, d, e, a, b, 18); R1(block, b, c, d, e, a, 19);
+ R2(block, a, b, c, d, e, 20); R2(block, e, a, b, c, d, 21); R2(block, d, e, a, b, c, 22); R2(block, c, d, e, a, b, 23);
+ R2(block, b, c, d, e, a, 24); R2(block, a, b, c, d, e, 25); R2(block, e, a, b, c, d, 26); R2(block, d, e, a, b, c, 27);
+ R2(block, c, d, e, a, b, 28); R2(block, b, c, d, e, a, 29); R2(block, a, b, c, d, e, 30); R2(block, e, a, b, c, d, 31);
+ R2(block, d, e, a, b, c, 32); R2(block, c, d, e, a, b, 33); R2(block, b, c, d, e, a, 34); R2(block, a, b, c, d, e, 35);
+ R2(block, e, a, b, c, d, 36); R2(block, d, e, a, b, c, 37); R2(block, c, d, e, a, b, 38); R2(block, b, c, d, e, a, 39);
+ R3(block, a, b, c, d, e, 40); R3(block, e, a, b, c, d, 41); R3(block, d, e, a, b, c, 42); R3(block, c, d, e, a, b, 43);
+ R3(block, b, c, d, e, a, 44); R3(block, a, b, c, d, e, 45); R3(block, e, a, b, c, d, 46); R3(block, d, e, a, b, c, 47);
+ R3(block, c, d, e, a, b, 48); R3(block, b, c, d, e, a, 49); R3(block, a, b, c, d, e, 50); R3(block, e, a, b, c, d, 51);
+ R3(block, d, e, a, b, c, 52); R3(block, c, d, e, a, b, 53); R3(block, b, c, d, e, a, 54); R3(block, a, b, c, d, e, 55);
+ R3(block, e, a, b, c, d, 56); R3(block, d, e, a, b, c, 57); R3(block, c, d, e, a, b, 58); R3(block, b, c, d, e, a, 59);
+ R4(block, a, b, c, d, e, 60); R4(block, e, a, b, c, d, 61); R4(block, d, e, a, b, c, 62); R4(block, c, d, e, a, b, 63);
+ R4(block, b, c, d, e, a, 64); R4(block, a, b, c, d, e, 65); R4(block, e, a, b, c, d, 66); R4(block, d, e, a, b, c, 67);
+ R4(block, c, d, e, a, b, 68); R4(block, b, c, d, e, a, 69); R4(block, a, b, c, d, e, 70); R4(block, e, a, b, c, d, 71);
+ R4(block, d, e, a, b, c, 72); R4(block, c, d, e, a, b, 73); R4(block, b, c, d, e, a, 74); R4(block, a, b, c, d, e, 75);
+ R4(block, e, a, b, c, d, 76); R4(block, d, e, a, b, c, 77); R4(block, c, d, e, a, b, 78); R4(block, b, c, d, e, a, 79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX *context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32 len)
+{
+ uint32 i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ ++context->count[1];
+ context->count[1] += len >> 29;
+ if (j + len > 63)
+ {
+ memcpy(&context->buffer[j], data, (i = 64 - j));
+ SHA1Transform(context->state, context->buffer);
+ for (; i + 63 < len; i += 64)
+ SHA1Transform(context->state, &data[i]);
+ j = 0;
+ }
+ else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX *context)
+{
+ uint32 i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; ++i)
+ finalcount[i] = static_cast<unsigned char>((context->count[i >= 4 ? 0 : 1] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */
+ SHA1Update(context, reinterpret_cast<const unsigned char *>("\200"), 1);
+ while ((context->count[0] & 504) != 448)
+ SHA1Update(context, reinterpret_cast<const unsigned char *>("\0"), 1);
+ SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+ for (i = 0; i < 20; ++i)
+ digest[i] = static_cast<unsigned char>((context->state[i>>2] >> ((3 - (i & 3)) * 8)) & 255);
+ /* Wipe variables */
+ i = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+ SHA1Transform(context->state, context->buffer);
+}
+
+/*****************************************************************************/
+/*****************************************************************************/
+
+/* Module stuff. */
+
+class ESHA1 : public Module
+{
+ public:
+ ESHA1(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(ENCRYPTION);
+
+ ModuleManager::Attach(I_OnEncrypt, this);
+ ModuleManager::Attach(I_OnEncryptCheckLen, this);
+ ModuleManager::Attach(I_OnDecrypt, this);
+ ModuleManager::Attach(I_OnCheckPassword, this);
+ }
+
+ EventReturn OnEncrypt(const std::string &src, std::string &dest)
+ {
+ SHA1_CTX context;
+ char *digest = new char[Config.PassLen];
+ std::string buf = "sha1:";
+ char cpass[1000];
+
+ memset(digest, 0, 32);
+
+ SHA1Init(&context);
+ SHA1Update(&context, reinterpret_cast<const unsigned char *>(src.c_str()), src.size());
+ SHA1Final(reinterpret_cast<unsigned char *>(digest), &context);
+
+ b64_encode(digest, 20, cpass, 1000);
+ buf += cpass;
+ Alog(LOG_DEBUG_2) << "(enc_sha1) hashed password from [" << src << "] to [" << buf << "]";
+ dest = buf;
+ delete [] digest;
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnDecrypt(const std::string &hashm, std::string &src, std::string &dest)
+ {
+ if (hashm != "sha1")
+ return EVENT_CONTINUE;
+ return EVENT_STOP;
+ }
+
+ EventReturn OnCheckPassword(const std::string &hashm, std::string &plaintext, std::string &password)
+ {
+ if (hashm != "sha1")
+ return EVENT_CONTINUE;
+ std::string buf;
+ this->OnEncrypt(plaintext, buf);
+ if (password == buf)
+ {
+ /* when we are NOT the first module in the list,
+ * we want to re-encrypt the pass with the new encryption
+ */
+ if (Config.EncModuleList.front() != this->name)
+ enc_encrypt(plaintext, password);
+ return EVENT_ALLOW;
+ }
+ return EVENT_STOP;
+ }
+};
+
+MODULE_INIT(ESHA1)
diff --git a/modules/core/enc_sha256.cpp b/modules/core/enc_sha256.cpp
new file mode 100644
index 000000000..fde7cf14c
--- /dev/null
+++ b/modules/core/enc_sha256.cpp
@@ -0,0 +1,316 @@
+/* This module generates and compares password hashes using SHA256 algorithms.
+ * To help reduce the risk of dictionary attacks, the code appends random bytes
+ * (so-called "salt") to the original plain text before generating hashes and
+ * stores this salt appended to the result. To verify another plain text value
+ * against the given hash, this module will retrieve the salt value from the
+ * password string and use it when computing a new hash of the plain text.
+ *
+ * If an intruder gets access to your system or uses a brute force attack,
+ * salt will not provide much value.
+ * IMPORTANT: DATA HASHES CANNOT BE "DECRYPTED" BACK TO PLAIN TEXT.
+ *
+ * Modified for Anope.
+ * (C) 2003-2010 Anope Team
+ * Contact us at team@anope.org
+ *
+ * Taken from InspIRCd ( www.inspircd.org )
+ * see http://wiki.inspircd.org/Credits
+ *
+ * This program is free but copyrighted software; see
+ * the file COPYING for details.
+ */
+
+/* FIPS 180-2 SHA-224/256/384/512 implementation
+ * Last update: 05/23/2005
+ * Issue date: 04/30/2005
+ *
+ * Copyright (C) 2005 Olivier Gay <olivier.gay@a3.epfl.ch>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "module.h"
+
+static const unsigned SHA256_DIGEST_SIZE = 256 / 8;
+static const unsigned SHA256_BLOCK_SIZE = 512 / 8;
+
+/** An sha256 context
+ */
+class SHA256Context
+{
+ public:
+ unsigned tot_len;
+ unsigned len;
+ unsigned char block[2 * SHA256_BLOCK_SIZE];
+ uint32 h[8];
+};
+
+inline static uint32 SHFR(uint32 x, uint32 n) { return x >> n; }
+inline static uint32 ROTR(uint32 x, uint32 n) { return (x >> n) | (x << ((sizeof(x) << 3) - n)); }
+inline static uint32 ROTL(uint32 x, uint32 n) { return (x << n) | (x >> ((sizeof(x) << 3) - n)); }
+inline static uint32 CH(uint32 x, uint32 y, uint32 z) { return (x & y) ^ (~x & z); }
+inline static uint32 MAJ(uint32 x, uint32 y, uint32 z) { return (x & y) ^ (x & z) ^ (y & z); }
+
+inline static uint32 SHA256_F1(uint32 x) { return ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22); }
+inline static uint32 SHA256_F2(uint32 x) { return ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25); }
+inline static uint32 SHA256_F3(uint32 x) { return ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3); }
+inline static uint32 SHA256_F4(uint32 x) { return ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10); }
+
+inline static void UNPACK32(unsigned x, unsigned char *str)
+{
+ str[3] = static_cast<uint8>(x);
+ str[2] = static_cast<uint8>(x >> 8);
+ str[1] = static_cast<uint8>(x >> 16);
+ str[0] = static_cast<uint8>(x >> 24);
+}
+
+inline static void PACK32(unsigned char *str, uint32 &x)
+{
+ x = static_cast<uint32>(str[3]) | static_cast<uint32>(str[2]) << 8 | static_cast<uint32>(str[1]) << 16 | static_cast<uint32>(str[0]) << 24;
+}
+
+/* Macros used for loops unrolling */
+
+inline static void SHA256_SCR(uint32 w[64], int i)
+{
+ w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16];
+}
+
+uint32 sha256_k[64] =
+{
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+class ESHA256 : public Module
+{
+ unsigned iv[8];
+ bool use_iv;
+
+ /* initializes the IV with a new random value */
+ void NewRandomIV()
+ {
+ for (int i = 0; i < 8; ++i)
+ iv[i] = getrandom32();
+ }
+
+ /* returns the IV as base64-encrypted string */
+ std::string GetIVString()
+ {
+ unsigned char buf[33];
+ char buf2[512];
+ for (int i = 0; i < 8; ++i)
+ UNPACK32(iv[i], &buf[i << 2]);
+ b64_encode(reinterpret_cast<char *>(buf), 32, buf2, 512);
+ return buf2;
+ }
+
+ /* splits the appended IV from the password string so it can be used for the next encryption */
+ /* password format: <hashmethod>:<password_b64>:<iv_b64> */
+ void GetIVFromPass(std::string &password)
+ {
+ size_t pos = password.find(":");
+ std::string buf(password, password.find(":", pos + 1) + 1, password.size());
+ unsigned char buf2[33];
+ b64_decode(buf.c_str(), reinterpret_cast<char *>(buf2), 33);
+ for (int i = 0 ; i < 8; ++i)
+ PACK32(&buf2[i<<2], iv[i]);
+ }
+
+ void SHA256Init(SHA256Context *ctx)
+ {
+ for (int i = 0; i < 8; ++i)
+ ctx->h[i] = iv[i];
+ ctx->len = 0;
+ ctx->tot_len = 0;
+ }
+
+ void SHA256Transform(SHA256Context *ctx, unsigned char *message, unsigned block_nb)
+ {
+ uint32 w[64], wv[8];
+ unsigned char *sub_block;
+ for (unsigned i = 1; i <= block_nb; ++i)
+ {
+ int j;
+ sub_block = message + ((i - 1) << 6);
+
+ for (j = 0; j < 16; ++j)
+ PACK32(&sub_block[j << 2], w[j]);
+ for (j = 16; j < 64; ++j)
+ SHA256_SCR(w, j);
+ for (j = 0; j < 8; ++j)
+ wv[j] = ctx->h[j];
+ for (j = 0; j < 64; ++j)
+ {
+ uint32 t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j];
+ uint32 t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]);
+ wv[7] = wv[6];
+ wv[6] = wv[5];
+ wv[5] = wv[4];
+ wv[4] = wv[3] + t1;
+ wv[3] = wv[2];
+ wv[2] = wv[1];
+ wv[1] = wv[0];
+ wv[0] = t1 + t2;
+ }
+ for (j = 0; j < 8; ++j)
+ ctx->h[j] += wv[j];
+ }
+ }
+
+ void SHA256Update(SHA256Context *ctx, const unsigned char *message, unsigned len)
+ {
+ /*
+ * XXX here be dragons!
+ * After many hours of pouring over this, I think I've found the problem.
+ * When Special created our module from the reference one, he used:
+ *
+ * unsigned rem_len = SHA256_BLOCK_SIZE - ctx->len;
+ *
+ * instead of the reference's version of:
+ *
+ * unsigned tmp_len = SHA256_BLOCK_SIZE - ctx->len;
+ * unsigned rem_len = len < tmp_len ? len : tmp_len;
+ *
+ * I've changed back to the reference version of this code, and it seems to work with no errors.
+ * So I'm inclined to believe this was the problem..
+ * -- w00t (January 06, 2008)
+ */
+ unsigned tmp_len = SHA256_BLOCK_SIZE - ctx->len, rem_len = len < tmp_len ? len : tmp_len;
+
+ memcpy(&ctx->block[ctx->len], message, rem_len);
+ if (ctx->len + len < SHA256_BLOCK_SIZE)
+ {
+ ctx->len += len;
+ return;
+ }
+ unsigned new_len = len - rem_len, block_nb = new_len / SHA256_BLOCK_SIZE;
+ unsigned char *shifted_message = new unsigned char[len - rem_len];
+ memcpy(shifted_message, message + rem_len, len - rem_len);
+ SHA256Transform(ctx, ctx->block, 1);
+ SHA256Transform(ctx, shifted_message, block_nb);
+ rem_len = new_len % SHA256_BLOCK_SIZE;
+ memcpy(ctx->block, &shifted_message[block_nb << 6], rem_len);
+ delete [] shifted_message;
+ ctx->len = rem_len;
+ ctx->tot_len += (block_nb + 1) << 6;
+ }
+
+ void SHA256Final(SHA256Context *ctx, unsigned char *digest)
+ {
+ unsigned block_nb = 1 + ((SHA256_BLOCK_SIZE - 9) < (ctx->len % SHA256_BLOCK_SIZE));
+ unsigned len_b = (ctx->tot_len + ctx->len) << 3;
+ unsigned pm_len = block_nb << 6;
+ memset(ctx->block + ctx->len, 0, pm_len - ctx->len);
+ ctx->block[ctx->len] = 0x80;
+ UNPACK32(len_b, ctx->block + pm_len - 4);
+ SHA256Transform(ctx, ctx->block, block_nb);
+ for (int i = 0 ; i < 8; ++i)
+ UNPACK32(ctx->h[i], &digest[i << 2]);
+ }
+
+/********** ANOPE ******/
+ public:
+ ESHA256(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(ENCRYPTION);
+
+ ModuleManager::Attach(I_OnEncrypt, this);
+ ModuleManager::Attach(I_OnDecrypt, this);
+ ModuleManager::Attach(I_OnCheckPassword, this);
+
+ use_iv = false;
+ }
+
+ EventReturn OnEncrypt(const std::string &src, std::string &dest)
+ {
+ char digest[SHA256_DIGEST_SIZE];
+ char cpass[1000];
+ SHA256Context ctx;
+ std::stringstream buf;
+
+ if (!use_iv)
+ NewRandomIV();
+ else
+ use_iv = false;
+
+ SHA256Init(&ctx);
+ SHA256Update(&ctx, reinterpret_cast<const unsigned char *>(src.c_str()), src.size());
+ SHA256Final(&ctx, reinterpret_cast<unsigned char *>(digest));
+
+ b64_encode(digest, SHA256_DIGEST_SIZE, cpass, 1000);
+ buf << "sha256:" << cpass << ":" << GetIVString();
+ Alog(LOG_DEBUG_2) << "(enc_sha256) hashed password from [" << src << "] to [" << buf.str() << " ]";
+ dest = buf.str();
+ return EVENT_ALLOW;
+ }
+
+ EventReturn OnDecrypt(const std::string &hashm, std::string &src, std::string &dest)
+ {
+ if (hashm != "sha256")
+ return EVENT_CONTINUE;
+ return EVENT_STOP;
+ }
+
+ EventReturn OnCheckPassword(const std::string &hashm, std::string &plaintext, std::string &password)
+ {
+ if (hashm != "sha256")
+ return EVENT_CONTINUE;
+ std::string buf;
+
+ GetIVFromPass(password);
+ use_iv = true;
+ this->OnEncrypt(plaintext, buf);
+
+ if (password == buf)
+ {
+ /* if we are NOT the first module in the list,
+ * we want to re-encrypt the pass with the new encryption
+ */
+ if (Config.EncModuleList.front() != this->name)
+ enc_encrypt(plaintext, password );
+ return EVENT_ALLOW;
+ }
+ return EVENT_STOP;
+ }
+};
+
+MODULE_INIT(ESHA256)
diff --git a/modules/core/hs_del.cpp b/modules/core/hs_del.cpp
new file mode 100644
index 000000000..9c3f8da62
--- /dev/null
+++ b/modules/core/hs_del.cpp
@@ -0,0 +1,73 @@
+/* HostServ 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 CommandHSDel : public Command
+{
+ public:
+ CommandHSDel() : Command("DEL", 1, 1, "hostserv/set")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na;
+ const char *nick = params[0].c_str();
+ if ((na = findnick(nick)))
+ {
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_HostServ, u, NICK_X_FORBIDDEN, nick);
+ return MOD_CONT;
+ }
+ Alog() << "vHost for user \002" << nick << "\002 deleted by oper \002" << u->nick << "\002";
+ FOREACH_MOD(I_OnDeleteVhost, OnDeleteVhost(na));
+ na->hostinfo.RemoveVhost();
+ notice_lang(Config.s_HostServ, u, HOST_DEL, nick);
+ }
+ else
+ notice_lang(Config.s_HostServ, u, HOST_NOREG, nick);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_DEL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_HostServ, u, "DEL", HOST_DEL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_DEL);
+ }
+};
+
+class HSDel : public Module
+{
+ public:
+ HSDel(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSDel());
+ }
+};
+
+MODULE_INIT(HSDel)
diff --git a/modules/core/hs_delall.cpp b/modules/core/hs_delall.cpp
new file mode 100644
index 000000000..cf249371a
--- /dev/null
+++ b/modules/core/hs_delall.cpp
@@ -0,0 +1,78 @@
+/* HostServ 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 CommandHSDelAll : public Command
+{
+ public:
+ CommandHSDelAll() : Command("DELALL", 1, 1, "hostserv/set")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ NickAlias *na;
+ if ((na = findnick(nick)))
+ {
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_HostServ, u, NICK_X_FORBIDDEN, nick);
+ return MOD_CONT;
+ }
+ FOREACH_MOD(I_OnDeleteVhost, OnDeleteVhost(na));
+ NickCore *nc = na->nc;
+ for (std::list<NickAlias *>::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it)
+ {
+ na = *it;
+ na->hostinfo.RemoveVhost();
+ }
+ Alog() << "vHosts for all nicks in group \002" << nc->display << "\002 deleted by oper \002" << u->nick << "\002";
+ notice_lang(Config.s_HostServ, u, HOST_DELALL, nc->display);
+ }
+ else
+ notice_lang(Config.s_HostServ, u, HOST_NOREG, nick);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_DELALL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_HostServ, u, "DELALL", HOST_DELALL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_DELALL);
+ }
+};
+
+class HSDelAll : public Module
+{
+ public:
+ HSDelAll(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSDelAll());
+ }
+};
+
+MODULE_INIT(HSDelAll)
diff --git a/modules/core/hs_group.cpp b/modules/core/hs_group.cpp
new file mode 100644
index 000000000..add5c1ecb
--- /dev/null
+++ b/modules/core/hs_group.cpp
@@ -0,0 +1,66 @@
+/* HostServ 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"
+
+extern int do_hs_sync(NickCore *nc, char *vIdent, char *hostmask, char *creator, time_t time);
+
+class CommandHSGroup : public Command
+{
+ public:
+ CommandHSGroup() : Command("GROUP", 0, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na = findnick(u->nick);
+ if (na && u->Account() == na->nc && na->hostinfo.HasVhost())
+ {
+ HostServSyncVhosts(na);
+ if (!na->hostinfo.GetIdent().empty())
+ notice_lang(Config.s_HostServ, u, HOST_IDENT_GROUP, u->Account()->display, na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str());
+ else
+ notice_lang(Config.s_HostServ, u, HOST_GROUP, u->Account()->display, na->hostinfo.GetHost().c_str());
+ }
+ else
+ notice_lang(Config.s_HostServ, u, HOST_NOT_ASSIGNED);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_GROUP);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_GROUP);
+ }
+};
+
+class HSGroup : public Module
+{
+ public:
+ HSGroup(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSGroup());
+ }
+};
+
+MODULE_INIT(HSGroup)
diff --git a/modules/core/hs_help.cpp b/modules/core/hs_help.cpp
new file mode 100644
index 000000000..b4b20e149
--- /dev/null
+++ b/modules/core/hs_help.cpp
@@ -0,0 +1,51 @@
+/* HostServ 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 CommandHSHelp : public Command
+{
+ public:
+ CommandHSHelp() : Command("HELP", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ mod_help_cmd(HostServ, u, params[0].c_str());
+ return MOD_CONT;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP, Config.s_HostServ);
+ for (CommandMap::const_iterator it = HostServ->Commands.begin(), it_end = HostServ->Commands.end(); it != it_end; ++it)
+ if (!Config.HidePrivilegedCommands || it->second->permission.empty() || (u->Account() && u->Account()->HasCommand(it->second->permission)))
+ it->second->OnServHelp(u);
+ }
+};
+
+class HSHelp : public Module
+{
+ public:
+ HSHelp(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSHelp());
+ }
+};
+
+MODULE_INIT(HSHelp)
diff --git a/modules/core/hs_list.cpp b/modules/core/hs_list.cpp
new file mode 100644
index 000000000..7e4d16cb8
--- /dev/null
+++ b/modules/core/hs_list.cpp
@@ -0,0 +1,132 @@
+/* HostServ 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 CommandHSList : public Command
+{
+ public:
+ CommandHSList() : Command("LIST", 0, 1, "hostserv/list")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string key = params.size() ? params[0] : "";
+ int from = 0, to = 0, counter = 1;
+ unsigned display_counter = 0;
+ tm *tm;
+ char buf[BUFSIZE];
+
+ /**
+ * Do a check for a range here, then in the next loop
+ * we'll only display what has been requested..
+ **/
+ if (!key.empty() && key[0] == '#')
+ {
+ size_t tmp = key.find('-');
+ if (tmp == ci::string::npos || tmp == key.size() || tmp == 1)
+ {
+ notice_lang(Config.s_HostServ, u, LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ for (unsigned i = 1, end = key.size(); i < end; ++i)
+ {
+ if (!isdigit(key[i]) && i != tmp)
+ {
+ notice_lang(Config.s_HostServ, u, LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ from = atoi(key.substr(1, tmp - 1).c_str());
+ to = atoi(key.substr(tmp + 1, key.size()).c_str());
+ }
+ }
+
+ for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it)
+ {
+ NickAlias *na = it->second;
+
+ if (!na->hostinfo.HasVhost())
+ continue;
+
+ if (!key.empty() && key[0] != '#')
+ {
+ if ((Anope::Match(na->nick, key) || Anope::Match(na->hostinfo.GetHost(), key.c_str())) && display_counter < Config.NSListMax)
+ {
+ ++display_counter;
+ time_t time = na->hostinfo.GetTime();
+ tm = localtime(&time);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ if (!na->hostinfo.GetIdent().empty())
+ notice_lang(Config.s_HostServ, u, HOST_IDENT_ENTRY, counter, na->nick, na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), buf);
+ else
+ notice_lang(Config.s_HostServ, u, HOST_ENTRY, counter, na->nick, na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), buf);
+ }
+ }
+ else
+ {
+ /**
+ * List the host if its in the display range, and not more
+ * than NSListMax records have been displayed...
+ **/
+ if (((counter >= from && counter <= to) || (!from && !to)) && display_counter < Config.NSListMax)
+ {
+ ++display_counter;
+ time_t time = na->hostinfo.GetTime();
+ tm = localtime(&time);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ if (!na->hostinfo.GetIdent().empty())
+ notice_lang(Config.s_HostServ, u, HOST_IDENT_ENTRY, counter, na->nick, na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), buf);
+ else
+ notice_lang(Config.s_HostServ, u, HOST_ENTRY, counter, na->nick, na->hostinfo.GetHost().c_str(), na->hostinfo.GetCreator().c_str(), buf);
+ }
+ }
+ ++counter;
+ }
+ if (!key.empty())
+ notice_lang(Config.s_HostServ, u, HOST_LIST_KEY_FOOTER, key.c_str(), display_counter);
+ else
+ {
+ if (from)
+ notice_lang(Config.s_HostServ, u, HOST_LIST_RANGE_FOOTER, from, to);
+ else
+ notice_lang(Config.s_HostServ, u, HOST_LIST_FOOTER, display_counter);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_LIST);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_LIST);
+ }
+};
+
+class HSList : public Module
+{
+ public:
+ HSList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSList());
+ }
+};
+
+MODULE_INIT(HSList)
diff --git a/modules/core/hs_off.cpp b/modules/core/hs_off.cpp
new file mode 100644
index 000000000..6d87a1288
--- /dev/null
+++ b/modules/core/hs_off.cpp
@@ -0,0 +1,62 @@
+/* HostServ 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 CommandHSOff : public Command
+{
+ public:
+ CommandHSOff() : Command("OFF", 0, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na = findnick(u->nick);
+
+ if (!na || !na->hostinfo.HasVhost())
+ notice_lang(Config.s_HostServ, u, HOST_NOT_ASSIGNED);
+ else
+ {
+ ircdproto->SendVhostDel(u);
+ notice_lang(Config.s_HostServ, u, HOST_OFF);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_OFF);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_OFF);
+ }
+};
+
+class HSOff : public Module
+{
+ public:
+ HSOff(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSOff());
+ }
+};
+
+MODULE_INIT(HSOff)
diff --git a/modules/core/hs_on.cpp b/modules/core/hs_on.cpp
new file mode 100644
index 000000000..d58348d8d
--- /dev/null
+++ b/modules/core/hs_on.cpp
@@ -0,0 +1,76 @@
+/* HostServ 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 CommandHSOn : public Command
+{
+ public:
+ CommandHSOn() : Command("ON", 0, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na = findnick(u->nick);
+ if (na && u->Account() == na->nc && na->hostinfo.HasVhost())
+ {
+ if (!na->hostinfo.GetIdent().empty())
+ notice_lang(Config.s_HostServ, u, HOST_IDENT_ACTIVATED, na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str());
+ else
+ notice_lang(Config.s_HostServ, u, HOST_ACTIVATED, na->hostinfo.GetHost().c_str());
+ ircdproto->SendVhost(u, na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str());
+ if (ircd->vhost)
+ {
+ if (u->vhost)
+ delete [] u->vhost;
+ u->vhost = sstrdup(na->hostinfo.GetHost().c_str());
+ }
+ if (ircd->vident)
+ {
+ if (!na->hostinfo.GetIdent().empty())
+ u->SetVIdent(na->hostinfo.GetIdent());
+ }
+ u->UpdateHost();
+ }
+ else
+ notice_lang(Config.s_HostServ, u, HOST_NOT_ASSIGNED);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_ON);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_ON);
+ }
+};
+
+class HSOn : public Module
+{
+ public:
+ HSOn(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSOn());
+ }
+};
+
+MODULE_INIT(HSOn)
diff --git a/modules/core/hs_set.cpp b/modules/core/hs_set.cpp
new file mode 100644
index 000000000..bf6c8788a
--- /dev/null
+++ b/modules/core/hs_set.cpp
@@ -0,0 +1,165 @@
+/* HostServ 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 CommandHSSet : public Command
+{
+ public:
+ CommandHSSet() : Command("SET", 2, 2, "hostserv/set")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ const char *rawhostmask = params[1].c_str();
+ char *hostmask = new char[Config.HostLen];
+
+ NickAlias *na;
+ int32 tmp_time;
+ char *s;
+
+ char *vIdent = NULL;
+
+ vIdent = myStrGetOnlyToken(rawhostmask, '@', 0); /* Get the first substring, @ as delimiter */
+ if (vIdent)
+ {
+ rawhostmask = myStrGetTokenRemainder(rawhostmask, '@', 1); /* get the remaining string */
+ if (!rawhostmask)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_SYNTAX, Config.s_HostServ);
+ delete [] vIdent;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ if (strlen(vIdent) > Config.UserLen)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_IDENTTOOLONG, Config.UserLen);
+ delete [] vIdent;
+ delete [] rawhostmask;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ else
+ {
+ for (s = vIdent; *s; ++s)
+ if (!isvalidchar(*s))
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_IDENT_ERROR);
+ delete [] vIdent;
+ delete [] rawhostmask;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ }
+ if (!ircd->vident)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_NO_VIDENT);
+ delete [] vIdent;
+ delete [] rawhostmask;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ }
+ if (strlen(rawhostmask) < Config.HostLen)
+ snprintf(hostmask, Config.HostLen, "%s", rawhostmask);
+ else
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_TOOLONG, Config.HostLen);
+ if (vIdent)
+ {
+ delete [] vIdent;
+ delete [] rawhostmask;
+ }
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+
+ if (!isValidHost(hostmask, 3))
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_ERROR);
+ if (vIdent)
+ {
+ delete [] vIdent;
+ delete [] rawhostmask;
+ }
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+
+ tmp_time = time(NULL);
+
+ if ((na = findnick(nick)))
+ {
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_HostServ, u, NICK_X_FORBIDDEN, nick);
+ if (vIdent)
+ {
+ delete [] vIdent;
+ delete [] rawhostmask;
+ }
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ Alog() << "vHost for user \002" << nick << "\002 set to \002" << (vIdent && ircd->vident ? vIdent : "") << (vIdent && ircd->vident ? "@" : "") << hostmask << " \002 by oper \002" << u->nick << "\002";
+
+ na->hostinfo.SetVhost(vIdent ? vIdent : "", hostmask, u->nick);
+ FOREACH_MOD(I_OnSetVhost, OnSetVhost(na));
+ if (vIdent)
+ notice_lang(Config.s_HostServ, u, HOST_IDENT_SET, nick, vIdent, hostmask);
+ else
+ notice_lang(Config.s_HostServ, u, HOST_SET, nick, hostmask);
+ }
+ else
+ notice_lang(Config.s_HostServ, u, HOST_NOREG, nick);
+ delete [] hostmask;
+ if (vIdent)
+ {
+ delete [] vIdent;
+ delete [] rawhostmask;
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_SET);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_HostServ, u, "SET", HOST_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_SET);
+ }
+};
+
+class HSSet : public Module
+{
+ public:
+ HSSet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSSet());
+ }
+};
+
+MODULE_INIT(HSSet)
diff --git a/modules/core/hs_setall.cpp b/modules/core/hs_setall.cpp
new file mode 100644
index 000000000..c0f3b1422
--- /dev/null
+++ b/modules/core/hs_setall.cpp
@@ -0,0 +1,164 @@
+/* HostServ 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 CommandHSSetAll : public Command
+{
+ public:
+ CommandHSSetAll() : Command("SETALL", 2, 2, "hostserv/set")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ const char *rawhostmask = params[1].c_str();
+ char *hostmask = new char[Config.HostLen];
+
+ NickAlias *na;
+ int32 tmp_time;
+ char *s;
+
+ char *vIdent = NULL;
+
+ if (!(na = findnick(nick)))
+ {
+ delete [] hostmask;
+ notice_lang(Config.s_HostServ, u, HOST_NOREG, nick);
+ return MOD_CONT;
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ {
+ delete [] hostmask;
+ notice_lang(Config.s_HostServ, u, NICK_X_FORBIDDEN, nick);
+ return MOD_CONT;
+ }
+
+ vIdent = myStrGetOnlyToken(rawhostmask, '@', 0); /* Get the first substring, @ as delimiter */
+ if (vIdent)
+ {
+ rawhostmask = myStrGetTokenRemainder(rawhostmask, '@', 1); /* get the remaining string */
+ if (!rawhostmask)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SETALL_SYNTAX, Config.s_HostServ);
+ delete [] vIdent;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ if (strlen(vIdent) > Config.UserLen)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_IDENTTOOLONG, Config.UserLen);
+ delete [] vIdent;
+ delete [] rawhostmask;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ else
+ {
+ for (s = vIdent; *s; ++s)
+ if (!isvalidchar(*s))
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_IDENT_ERROR);
+ delete [] vIdent;
+ delete [] rawhostmask;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ }
+ if (!ircd->vident)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_NO_VIDENT);
+ delete [] vIdent;
+ delete [] rawhostmask;
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+ }
+
+ if (strlen(rawhostmask) < Config.HostLen)
+ snprintf(hostmask, Config.HostLen, "%s", rawhostmask);
+ else
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_TOOLONG, Config.HostLen);
+ if (vIdent)
+ {
+ delete [] vIdent;
+ delete [] rawhostmask;
+ }
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+
+ if (!isValidHost(hostmask, 3))
+ {
+ notice_lang(Config.s_HostServ, u, HOST_SET_ERROR);
+ if (vIdent)
+ {
+ delete [] vIdent;
+ delete [] rawhostmask;
+ }
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+
+ tmp_time = time(NULL);
+
+ Alog() << "vHost for all nicks in group \002" << nick << "\002 set to \002" << (vIdent && ircd->vident ? vIdent : "") << (vIdent && ircd->vident ? "@" : "") << hostmask << " \002 by oper \002" << u->nick << "\002";
+
+ na->hostinfo.SetVhost(vIdent ? vIdent : "", hostmask, u->nick);
+ HostServSyncVhosts(na);
+ FOREACH_MOD(I_OnSetVhost, OnSetVhost(na));
+ if (vIdent)
+ notice_lang(Config.s_HostServ, u, HOST_IDENT_SETALL, nick, vIdent, hostmask);
+ else
+ notice_lang(Config.s_HostServ, u, HOST_SETALL, nick, hostmask);
+ if (vIdent)
+ {
+ delete [] vIdent;
+ delete [] rawhostmask;
+ }
+ delete [] hostmask;
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_HostServ, u, HOST_HELP_SETALL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_HostServ, u, "SETALL", HOST_SETALL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_HostServ, u, HOST_HELP_CMD_SETALL);
+ }
+};
+
+class HSSetAll : public Module
+{
+ public:
+ HSSetAll(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(HostServ, new CommandHSSetAll());
+ }
+};
+
+MODULE_INIT(HSSetAll)
diff --git a/modules/core/ms_cancel.cpp b/modules/core/ms_cancel.cpp
new file mode 100644
index 000000000..af26ab6b8
--- /dev/null
+++ b/modules/core/ms_cancel.cpp
@@ -0,0 +1,87 @@
+/* MemoServ 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"
+
+void myMemoServHelp(User *u);
+
+class CommandMSCancel : public Command
+{
+ public:
+ CommandMSCancel() : Command("CANCEL", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ int ischan;
+ int isforbid;
+ const char *name = params[0].c_str();
+ MemoInfo *mi;
+
+ if (!u->IsRecognized())
+ notice_lang(Config.s_MemoServ, u, NICK_IDENTIFY_REQUIRED, Config.s_NickServ);
+ else if (!(mi = getmemoinfo(name, &ischan, &isforbid)))
+ {
+ if (isforbid)
+ notice_lang(Config.s_MemoServ, u, ischan ? CHAN_X_FORBIDDEN : NICK_X_FORBIDDEN, name);
+ else
+ notice_lang(Config.s_MemoServ, u, ischan ? CHAN_X_NOT_REGISTERED : NICK_X_NOT_REGISTERED, name);
+ }
+ else
+ {
+ int i;
+
+ for (i = mi->memos.size() - 1; i >= 0; --i)
+ if ((mi->memos[i]->HasFlag(MF_UNREAD)) && !stricmp(mi->memos[i]->sender.c_str(), u->Account()->display) && !mi->memos[i]->HasFlag(MF_NOTIFYS))
+ {
+ FOREACH_MOD(I_OnMemoDel, OnMemoDel(findnick(name)->nc, mi, mi->memos[i]->number));
+ delmemo(mi, mi->memos[i]->number);
+ notice_lang(Config.s_MemoServ, u, MEMO_CANCELLED, name);
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_MemoServ, u, MEMO_CANCEL_NONE);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_CANCEL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "CANCEL", MEMO_CANCEL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_CANCEL);
+ }
+};
+
+class MSCancel : public Module
+{
+ public:
+ MSCancel(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->AddCommand(MemoServ, new CommandMSCancel());
+ }
+};
+
+MODULE_INIT(MSCancel)
diff --git a/modules/core/ms_check.cpp b/modules/core/ms_check.cpp
new file mode 100644
index 000000000..62400641b
--- /dev/null
+++ b/modules/core/ms_check.cpp
@@ -0,0 +1,106 @@
+/* MemoServ 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 CommandMSCheck : public Command
+{
+ public:
+ CommandMSCheck() : Command("CHECK", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na = NULL;
+ MemoInfo *mi = NULL;
+ int i, found = 0;
+ const char *recipient = params[0].c_str();
+ struct tm *tm;
+ char timebuf[64];
+
+ if (!u->IsRecognized())
+ {
+ notice_lang(Config.s_MemoServ, u, NICK_IDENTIFY_REQUIRED, Config.s_NickServ);
+ return MOD_CONT;
+ }
+ else if (!(na = findnick(recipient)))
+ {
+ notice_lang(Config.s_MemoServ, u, NICK_X_NOT_REGISTERED, recipient);
+ return MOD_CONT;
+ }
+
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_MemoServ, u, NICK_X_FORBIDDEN, recipient);
+ return MOD_CONT;
+ }
+
+ mi = &na->nc->memos;
+
+ /* Okay, I know this looks strange but we wanna get the LAST memo, so we
+ have to loop backwards */
+
+ for (i = mi->memos.size() - 1; i >= 0; --i)
+ {
+ if (!stricmp(mi->memos[i]->sender.c_str(), u->Account()->display))
+ {
+ found = 1; /* Yes, we've found the memo */
+
+ tm = localtime(&mi->memos[i]->time);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+
+ if (mi->memos[i]->HasFlag(MF_UNREAD))
+ notice_lang(Config.s_MemoServ, u, MEMO_CHECK_NOT_READ, na->nick, timebuf);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_CHECK_READ, na->nick, timebuf);
+ break;
+ }
+ }
+
+ if (!found)
+ notice_lang(Config.s_MemoServ, u, MEMO_CHECK_NO_MEMO, na->nick);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_CHECK);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "CHECK", MEMO_CHECK_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_CHECK);
+ }
+};
+
+class MSCheck : public Module
+{
+ public:
+ MSCheck(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSCheck());
+ }
+};
+
+MODULE_INIT(MSCheck)
diff --git a/modules/core/ms_del.cpp b/modules/core/ms_del.cpp
new file mode 100644
index 000000000..270bac4f3
--- /dev/null
+++ b/modules/core/ms_del.cpp
@@ -0,0 +1,162 @@
+/* MemoServ 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 MemoDelCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ MemoInfo *mi;
+ public:
+ MemoDelCallback(User *_u, ChannelInfo *_ci, MemoInfo *_mi, const std::string &list) : NumberList(list, true), u(_u), ci(_ci), mi(_mi)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > mi->memos.size())
+ return;
+
+ if (ci)
+ FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, Number - 1));
+ else
+ FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, Number - 1));
+
+ delmemo(mi, Number - 1);
+ }
+};
+
+class CommandMSDel : public Command
+{
+ public:
+ CommandMSDel() : Command("DEL", 0, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ MemoInfo *mi;
+ ChannelInfo *ci = NULL;
+ ci::string numstr = params.size() ? params[0] : "", chan;
+ unsigned i, end;
+ int last;
+
+ if (!numstr.empty() && numstr[0] == '#')
+ {
+ chan = numstr;
+ numstr = params.size() > 1 ? params[1] : "";
+
+ if (!(ci = cs_findchan(chan)))
+ {
+ notice_lang(Config.s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan.c_str());
+ return MOD_CONT;
+ }
+ else if (readonly)
+ {
+ notice_lang(Config.s_MemoServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+ else if (!check_access(u, ci, CA_MEMO))
+ {
+ notice_lang(Config.s_MemoServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ mi = &ci->memos;
+ }
+ else
+ mi = &u->Account()->memos;
+ if (numstr.empty() || (!isdigit(numstr[0]) && numstr != "ALL" && numstr != "LAST"))
+ this->OnSyntaxError(u, numstr);
+ else if (mi->memos.empty())
+ {
+ if (!chan.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_X_HAS_NO_MEMOS, chan.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_HAVE_NO_MEMOS);
+ }
+ else
+ {
+ if (isdigit(numstr[0]))
+ {
+ MemoDelCallback list(u, ci, mi, numstr.c_str());
+ list.Process();
+ }
+ else if (numstr == "LAST")
+ {
+ /* Delete last memo. */
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ last = mi->memos[i]->number;
+ if (ci)
+ FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, last));
+ else
+ FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, last));
+ delmemo(mi, last);
+ notice_lang(Config.s_MemoServ, u, MEMO_DELETED_ONE, last);
+ }
+ else
+ {
+ if (ci)
+ FOREACH_MOD(I_OnMemoDel, OnMemoDel(ci, mi, 0));
+ else
+ FOREACH_MOD(I_OnMemoDel, OnMemoDel(u->Account(), mi, 0));
+ /* Delete all memos. */
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ {
+ delete [] mi->memos[i]->text;
+ delete mi->memos[i];
+ }
+ mi->memos.clear();
+ if (!chan.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_CHAN_DELETED_ALL, chan.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_DELETED_ALL);
+ }
+
+ /* Reset the order */
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ mi->memos[i]->number = i + 1;
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_DEL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "DEL", MEMO_DEL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_DEL);
+ }
+};
+
+class MSDel : public Module
+{
+ public:
+ MSDel(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSDel());
+ }
+};
+
+MODULE_INIT(MSDel)
diff --git a/modules/core/ms_help.cpp b/modules/core/ms_help.cpp
new file mode 100644
index 000000000..41cdaafb3
--- /dev/null
+++ b/modules/core/ms_help.cpp
@@ -0,0 +1,52 @@
+/* MemoServ 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 CommandMSHelp : public Command
+{
+ public:
+ CommandMSHelp() : Command("HELP", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ mod_help_cmd(MemoServ, u, params[0].c_str());
+ return MOD_CONT;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_HEADER);
+ for (CommandMap::const_iterator it = MemoServ->Commands.begin(), it_end = MemoServ->Commands.end(); it != it_end; ++it)
+ if (!Config.HidePrivilegedCommands || it->second->permission.empty() || (u->Account() && u->Account()->HasCommand(it->second->permission)))
+ it->second->OnServHelp(u);
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_FOOTER, Config.s_ChanServ);
+ }
+};
+
+class MSHelp : public Module
+{
+ public:
+ MSHelp(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSHelp());
+ }
+};
+
+MODULE_INIT(MSHelp)
diff --git a/modules/core/ms_info.cpp b/modules/core/ms_info.cpp
new file mode 100644
index 000000000..1a368baf9
--- /dev/null
+++ b/modules/core/ms_info.cpp
@@ -0,0 +1,215 @@
+/* MemoServ 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 CommandMSInfo : public Command
+{
+ public:
+ CommandMSInfo() : Command("INFO", 0, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ MemoInfo *mi;
+ NickAlias *na = NULL;
+ ChannelInfo *ci = NULL;
+ const char *name = params.size() ? params[0].c_str() : NULL;
+ int hardmax = 0;
+
+ if (name && *name != '#' && u->Account()->HasPriv("memoserv/info"))
+ {
+ na = findnick(name);
+ if (!na)
+ {
+ notice_lang(Config.s_MemoServ, u, NICK_X_NOT_REGISTERED, name);
+ return MOD_CONT;
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_MemoServ, u, NICK_X_FORBIDDEN, name);
+ return MOD_CONT;
+ }
+ mi = &na->nc->memos;
+ hardmax = na->nc->HasFlag(NI_MEMO_HARDMAX) ? 1 : 0;
+ }
+ else if (name && *name == '#')
+ {
+ if (!(ci = cs_findchan(name)))
+ {
+ notice_lang(Config.s_MemoServ, u, CHAN_X_NOT_REGISTERED, name);
+ return MOD_CONT;
+ }
+ else if (!check_access(u, ci, CA_MEMO))
+ {
+ notice_lang(Config.s_MemoServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ mi = &ci->memos;
+ hardmax = ci->HasFlag(CI_MEMO_HARDMAX) ? 1 : 0;
+ }
+ else if (name) /* It's not a chan and we aren't services admin */
+ {
+ notice_lang(Config.s_MemoServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ else
+ {
+ mi = &u->Account()->memos;
+ hardmax = u->Account()->HasFlag(NI_MEMO_HARDMAX) ? 1 : 0;
+ }
+
+ if (name && (ci || na->nc != u->Account()))
+ {
+ if (mi->memos.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_NO_MEMOS, name);
+ else if (mi->memos.size() == 1)
+ {
+ if (mi->memos[0]->HasFlag(MF_UNREAD))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_MEMO_UNREAD, name);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_MEMO, name);
+ }
+ else
+ {
+ int count = 0, i, end;
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ if (mi->memos[i]->HasFlag(MF_UNREAD))
+ ++count;
+ if (count == mi->memos.size())
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_MEMOS_ALL_UNREAD, name, count);
+ else if (!count)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_MEMOS, name, mi->memos.size());
+ else if (count == 1)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_MEMOS_ONE_UNREAD, name, mi->memos.size());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_MEMOS_SOME_UNREAD, name, mi->memos.size(), count);
+ }
+ if (!mi->memomax)
+ {
+ if (hardmax)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_HARD_LIMIT, name, mi->memomax);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_LIMIT, name, mi->memomax);
+ }
+ else if (mi->memomax > 0)
+ {
+ if (hardmax)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_HARD_LIMIT, name, mi->memomax);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_LIMIT, name, mi->memomax);
+ }
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_NO_LIMIT, name);
+
+ /* I ripped this code out of ircservices 4.4.5, since I didn't want
+ to rewrite the whole thing (it pisses me off). */
+ if (na)
+ {
+ if (na->nc->HasFlag(NI_MEMO_RECEIVE) && na->nc->HasFlag(NI_MEMO_SIGNON))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_NOTIFY_ON, name);
+ else if (na->nc->HasFlag(NI_MEMO_RECEIVE))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_NOTIFY_RECEIVE, name);
+ else if (na->nc->HasFlag(NI_MEMO_SIGNON))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_NOTIFY_SIGNON, name);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_X_NOTIFY_OFF, name);
+ }
+ }
+ else /* !name || (!ci || na->nc == u->Account()) */
+ {
+ if (mi->memos.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_NO_MEMOS);
+ else if (mi->memos.size() == 1)
+ {
+ if (mi->memos[0]->HasFlag(MF_UNREAD))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_MEMO_UNREAD);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_MEMO);
+ }
+ else
+ {
+ int count = 0, i, end;
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ if (mi->memos[i]->HasFlag(MF_UNREAD))
+ ++count;
+ if (count == mi->memos.size())
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_MEMOS_ALL_UNREAD, count);
+ else if (!count)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_MEMOS, mi->memos.size());
+ else if (count == 1)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_MEMOS_ONE_UNREAD, mi->memos.size());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_MEMOS_SOME_UNREAD, mi->memos.size(), count);
+ }
+
+ if (!mi->memomax)
+ {
+ if (!u->Account()->IsServicesOper() && hardmax)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_HARD_LIMIT_ZERO);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_LIMIT_ZERO);
+ }
+ else if (mi->memomax > 0)
+ {
+ if (!u->Account()->IsServicesOper() && hardmax)
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_HARD_LIMIT, mi->memomax);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_LIMIT, mi->memomax);
+ }
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_NO_LIMIT);
+
+ /* Ripped too. But differently because of a seg fault (loughs) */
+ if (u->Account()->HasFlag(NI_MEMO_RECEIVE) && u->Account()->HasFlag(NI_MEMO_SIGNON))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_NOTIFY_ON);
+ else if (u->Account()->HasFlag(NI_MEMO_RECEIVE))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_NOTIFY_RECEIVE);
+ else if (u->Account()->HasFlag(NI_MEMO_SIGNON))
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_NOTIFY_SIGNON);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_INFO_NOTIFY_OFF);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_MemoServ, u, MEMO_SERVADMIN_HELP_INFO);
+ else
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_INFO);
+
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_INFO);
+ }
+};
+
+class MSInfo : public Module
+{
+ public:
+ MSInfo(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSInfo());
+ }
+};
+
+MODULE_INIT(MSInfo)
diff --git a/modules/core/ms_list.cpp b/modules/core/ms_list.cpp
new file mode 100644
index 000000000..6630c28db
--- /dev/null
+++ b/modules/core/ms_list.cpp
@@ -0,0 +1,176 @@
+/* MemoServ 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 MemoListCallback : public NumberList
+{
+ User *u;
+ ChannelInfo *ci;
+ MemoInfo *mi;
+ bool SentHeader;
+ public:
+ MemoListCallback(User *_u, ChannelInfo *_ci, MemoInfo *_mi, const std::string &list) : NumberList(list, false), u(_u), ci(_ci), mi(_mi), SentHeader(false)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > mi->memos.size())
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ if (ci)
+ notice_lang(Config.s_MemoServ, u, MEMO_LIST_CHAN_MEMOS, ci->name.c_str(), Config.s_MemoServ, ci->name.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_LIST_MEMOS, u->nick.c_str(), Config.s_MemoServ);
+
+ notice_lang(Config.s_MemoServ, u, MEMO_LIST_HEADER);
+ }
+
+ DoList(u, ci, mi, Number - 1);
+ }
+
+ static void DoList(User *u, ChannelInfo *ci, MemoInfo *mi, unsigned index)
+ {
+ Memo *m = mi->memos[index];
+ struct tm tm = *localtime(&m->time);
+ char timebuf[64];
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, &tm);
+ timebuf[sizeof(timebuf) - 1] = 0; /* just in case */
+ notice_lang(Config.s_MemoServ, u, MEMO_LIST_FORMAT, (m->HasFlag(MF_UNREAD)) ? '*' : ' ', m->number, m->sender.c_str(), timebuf);
+ }
+};
+
+class CommandMSList : public Command
+{
+ public:
+ CommandMSList() : Command("LIST", 0, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string param = params.size() ? params[0] : "", chan;
+ ChannelInfo *ci;
+ MemoInfo *mi;
+ int i, end;
+
+ if (!param.empty() && param[0] == '#')
+ {
+ chan = param;
+ param = params.size() > 1 ? params[1] : "";
+
+ if (!(ci = cs_findchan(chan)))
+ {
+ notice_lang(Config.s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan.c_str());
+ return MOD_CONT;
+ }
+ else if (!check_access(u, ci, CA_MEMO))
+ {
+ notice_lang(Config.s_MemoServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ mi = &ci->memos;
+ }
+ else
+ mi = &u->Account()->memos;
+ if (!param.empty() && !isdigit(param[0]) && param != "NEW")
+ this->OnSyntaxError(u, param);
+ else if (!mi->memos.size())
+ {
+ if (!chan.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_X_HAS_NO_MEMOS, chan.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_HAVE_NO_MEMOS);
+ }
+ else
+ {
+ if (!param.empty() && isdigit(param[0]))
+ {
+ MemoListCallback list(u, ci, mi, param.c_str());
+ list.Process();
+ }
+ else
+ {
+ if (!param.empty())
+ {
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ if (mi->memos[i]->HasFlag(MF_UNREAD))
+ break;
+ if (i == end)
+ {
+ if (!chan.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_X_HAS_NO_NEW_MEMOS, chan.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_HAVE_NO_NEW_MEMOS);
+ return MOD_CONT;
+ }
+ }
+
+ bool SentHeader = false;
+
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ {
+ if (!param.empty() && !(mi->memos[i]->HasFlag(MF_UNREAD)))
+ continue;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ if (ci)
+ notice_lang(Config.s_MemoServ, u, !param.empty() ? MEMO_LIST_CHAN_NEW_MEMOS : MEMO_LIST_CHAN_MEMOS, ci->name.c_str(), Config.s_MemoServ, ci->name.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, !param.empty() ? MEMO_LIST_NEW_MEMOS : MEMO_LIST_MEMOS, u->nick.c_str(), Config.s_MemoServ);
+ notice_lang(Config.s_MemoServ, u, MEMO_LIST_HEADER);
+ }
+
+ MemoListCallback::DoList(u, ci, mi, i);
+ }
+ }
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_LIST);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "LIST", MEMO_LIST_SYNTAX);
+ }
+
+ void OnServCommand(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_LIST);
+ }
+};
+
+class MSList : public Module
+{
+ public:
+ MSList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSList());
+ }
+};
+
+MODULE_INIT(MSList)
diff --git a/modules/core/ms_read.cpp b/modules/core/ms_read.cpp
new file mode 100644
index 000000000..d779471b3
--- /dev/null
+++ b/modules/core/ms_read.cpp
@@ -0,0 +1,159 @@
+/* MemoServ 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 MemoListCallback : public NumberList
+{
+ User *u;
+ MemoInfo *mi;
+ public:
+ MemoListCallback(User *_u, MemoInfo *_mi, const std::string &numlist) : NumberList(numlist, false), u(_u), mi(_mi)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > mi->memos.size())
+ return;
+
+ MemoListCallback::DoRead(u, mi, NULL, Number - 1);
+ }
+
+ static void DoRead(User *u, MemoInfo *mi, ChannelInfo *ci, unsigned index)
+ {
+ Memo *m = mi->memos[index];
+ struct tm tm = *localtime(&m->time);
+ char timebuf[64];
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, &tm);
+ timebuf[sizeof(timebuf) - 1] = 0;
+ if (ci)
+ notice_lang(Config.s_MemoServ, u, MEMO_CHAN_HEADER, m->number, m->sender.c_str(), timebuf, Config.s_MemoServ, ci->name.c_str(), m->number);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_HEADER, m->number, m->sender.c_str(), timebuf, Config.s_MemoServ, m->number);
+ notice_lang(Config.s_MemoServ, u, MEMO_TEXT, m->text);
+ m->UnsetFlag(MF_UNREAD);
+
+ /* Check if a receipt notification was requested */
+ if (m->HasFlag(MF_RECEIPT))
+ rsend_notify(u, m, ci ? ci->name.c_str() : NULL);
+ }
+};
+
+class CommandMSRead : public Command
+{
+ public:
+ CommandMSRead() : Command("READ", 0, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ MemoInfo *mi;
+ ChannelInfo *ci = NULL;
+ ci::string numstr = params.size() ? params[0] : "", chan;
+ int num;
+
+ if (!numstr.empty() && numstr[0] == '#')
+ {
+ chan = numstr;
+ numstr = params.size() > 1 ? params[1] : "";
+
+ if (!(ci = cs_findchan(chan)))
+ {
+ notice_lang(Config.s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan.c_str());
+ return MOD_CONT;
+ }
+ else if (!check_access(u, ci, CA_MEMO))
+ {
+ notice_lang(Config.s_MemoServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ mi = &ci->memos;
+ }
+ else
+ mi = &u->Account()->memos;
+ num = !numstr.empty() ? atoi(numstr.c_str()) : -1;
+ if (numstr.empty() || (numstr != "LAST" && numstr != "NEW" && num <= 0))
+ this->OnSyntaxError(u, numstr);
+ else if (mi->memos.empty())
+ {
+ if (!chan.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_X_HAS_NO_MEMOS, chan.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_HAVE_NO_MEMOS);
+ }
+ else {
+ int i, end;
+
+ if (numstr == "NEW")
+ {
+ int readcount = 0;
+ for (i = 0, end = mi->memos.size(); i < end; ++i)
+ if (mi->memos[i]->HasFlag(MF_UNREAD))
+ {
+ MemoListCallback::DoRead(u, mi, ci, i);
+ ++readcount;
+ }
+ if (!readcount)
+ {
+ if (!chan.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_X_HAS_NO_NEW_MEMOS, chan.c_str());
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_HAVE_NO_NEW_MEMOS);
+ }
+ }
+ else if (numstr == "LAST")
+ {
+ for (i = 0, end = mi->memos.size() - 1; i < end; ++i);
+ MemoListCallback::DoRead(u, mi, ci, i);
+ }
+ else /* number[s] */
+ {
+ MemoListCallback list(u, mi, numstr.c_str());
+ list.Process();
+ }
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_READ);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "READ", MEMO_READ_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_READ);
+ }
+};
+
+class MSRead : public Module
+{
+ public:
+ MSRead(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSRead());
+ }
+};
+
+MODULE_INIT(MSRead)
diff --git a/modules/core/ms_rsend.cpp b/modules/core/ms_rsend.cpp
new file mode 100644
index 000000000..0c21ac5fc
--- /dev/null
+++ b/modules/core/ms_rsend.cpp
@@ -0,0 +1,90 @@
+/* MemoServ 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 CommandMSRSend : public Command
+{
+ public:
+ CommandMSRSend() : Command("RSEND", 2, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ const char *text = params[1].c_str();
+ NickAlias *na = NULL;
+ int z = 3;
+
+ /* prevent user from rsend to themselves */
+ if ((na = findnick(nick)) && na->nc == u->Account())
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_NO_RSEND_SELF);
+ return MOD_CONT;
+ }
+
+ if (Config.MSMemoReceipt == 1)
+ {
+ /* Services opers and above can use rsend */
+ if (u->Account()->IsServicesOper())
+ memo_send(u, nick, text, z);
+ else
+ notice_lang(Config.s_MemoServ, u, ACCESS_DENIED);
+ }
+ else if (Config.MSMemoReceipt == 2)
+ /* Everybody can use rsend */
+ memo_send(u, nick, text, z);
+ else
+ {
+ /* rsend has been disabled */
+ Alog(LOG_DEBUG) << "MSMemoReceipt is set misconfigured to " << Config.MSMemoReceipt;
+ notice_lang(Config.s_MemoServ, u, MEMO_RSEND_DISABLED);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_RSEND);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "RSEND", MEMO_RSEND_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_RSEND);
+ }
+};
+
+class MSRSend : public Module
+{
+ public:
+ MSRSend(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!Config.MSMemoReceipt)
+ throw ModuleException("Don't like memo reciepts, or something.");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSRSend());
+ }
+};
+
+MODULE_INIT(MSRSend)
diff --git a/modules/core/ms_send.cpp b/modules/core/ms_send.cpp
new file mode 100644
index 000000000..5f55fc774
--- /dev/null
+++ b/modules/core/ms_send.cpp
@@ -0,0 +1,61 @@
+/* MemoServ 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 CommandMSSend : public Command
+{
+ public:
+ CommandMSSend() : Command("SEND", 2, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ const char *text = params[1].c_str();
+ int z = 0;
+ memo_send(u, nick, text, z);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_SEND);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "SEND", MEMO_SEND_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_SEND);
+ }
+};
+
+class MSSend : public Module
+{
+ public:
+ MSSend(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSSend());
+ }
+};
+
+MODULE_INIT(MSSend)
diff --git a/modules/core/ms_sendall.cpp b/modules/core/ms_sendall.cpp
new file mode 100644
index 000000000..16b95f928
--- /dev/null
+++ b/modules/core/ms_sendall.cpp
@@ -0,0 +1,77 @@
+/* MemoServ 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 CommandMSSendAll : public Command
+{
+ public:
+ CommandMSSendAll() : Command("SENDALL", 1, 1, "memoserv/sendall")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ int z = 1;
+ const char *text = params[0].c_str();
+
+ if (readonly)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SEND_DISABLED);
+ return MOD_CONT;
+ }
+
+ NickAlias *na = findnick(u->nick);
+
+ for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it)
+ {
+ NickCore *nc = it->second;
+
+ if ((na && na->nc == nc) || stricmp(u->nick.c_str(), nc->display))
+ memo_send(u, nc->display, text, z);
+ }
+
+ notice_lang(Config.s_MemoServ, u, MEMO_MASS_SENT);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_SENDALL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "SENDALL", MEMO_SEND_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_SENDALL);
+ }
+};
+
+class MSSendAll : public Module
+{
+ public:
+ MSSendAll(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSSendAll());
+ }
+};
+
+MODULE_INIT(MSSendAll)
diff --git a/modules/core/ms_set.cpp b/modules/core/ms_set.cpp
new file mode 100644
index 000000000..11ba9d9cc
--- /dev/null
+++ b/modules/core/ms_set.cpp
@@ -0,0 +1,272 @@
+/* MemoServ 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 CommandMSSet : public Command
+{
+ private:
+ CommandReturn DoNotify(User *u, const std::vector<ci::string> &params, MemoInfo *mi)
+ {
+ ci::string param = params[1];
+
+ if (param == "ON")
+ {
+ u->Account()->SetFlag(NI_MEMO_SIGNON);
+ u->Account()->SetFlag(NI_MEMO_RECEIVE);
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_NOTIFY_ON, Config.s_MemoServ);
+ }
+ else if (param == "LOGON")
+ {
+ u->Account()->SetFlag(NI_MEMO_SIGNON);
+ u->Account()->UnsetFlag(NI_MEMO_RECEIVE);
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_NOTIFY_LOGON, Config.s_MemoServ);
+ }
+ else if (param == "NEW")
+ {
+ u->Account()->UnsetFlag(NI_MEMO_SIGNON);
+ u->Account()->SetFlag(NI_MEMO_RECEIVE);
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_NOTIFY_NEW, Config.s_MemoServ);
+ }
+ else if (param == "MAIL")
+ {
+ if (u->Account()->email)
+ {
+ u->Account()->SetFlag(NI_MEMO_MAIL);
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_NOTIFY_MAIL);
+ }
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_NOTIFY_INVALIDMAIL);
+ }
+ else if (param == "NOMAIL")
+ {
+ u->Account()->UnsetFlag(NI_MEMO_MAIL);
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_NOTIFY_NOMAIL);
+ }
+ else if (param == "OFF")
+ {
+ u->Account()->UnsetFlag(NI_MEMO_SIGNON);
+ u->Account()->UnsetFlag(NI_MEMO_RECEIVE);
+ u->Account()->UnsetFlag(NI_MEMO_MAIL);
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_NOTIFY_OFF, Config.s_MemoServ);
+ }
+ else
+ syntax_error(Config.s_MemoServ, u, "SET NOTIFY", MEMO_SET_NOTIFY_SYNTAX);
+ return MOD_CONT;
+ }
+
+ CommandReturn DoLimit(User *u, const std::vector<ci::string> &params, MemoInfo *mi)
+ {
+ ci::string p1 = params[1];
+ ci::string p2 = params.size() > 2 ? params[2] : "";
+ ci::string p3 = params.size() > 3 ? params[3] : "";
+ ci::string user, chan;
+ int32 limit;
+ NickCore *nc = u->Account();
+ ChannelInfo *ci = NULL;
+ bool is_servadmin = u->Account()->HasPriv("memoserv/set-limit");
+
+ if (p1[0] == '#')
+ {
+ chan = p1;
+ p1 = p2;
+ p2 = p3;
+ p3 = params.size() > 4 ? params[4] : "";
+ if (!(ci = cs_findchan(chan)))
+ {
+ notice_lang(Config.s_MemoServ, u, CHAN_X_NOT_REGISTERED, chan.c_str());
+ return MOD_CONT;
+ }
+ else if (!is_servadmin && !check_access(u, ci, CA_MEMO))
+ {
+ notice_lang(Config.s_MemoServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ mi = &ci->memos;
+ }
+ if (is_servadmin)
+ {
+ if (!p2.empty() && p2 != "HARD" && chan.empty())
+ {
+ NickAlias *na;
+ if (!(na = findnick(p1)))
+ {
+ notice_lang(Config.s_MemoServ, u, NICK_X_NOT_REGISTERED, p1.c_str());
+ return MOD_CONT;
+ }
+ user = p1;
+ mi = &na->nc->memos;
+ nc = na->nc;
+ p1 = p2;
+ p2 = p3;
+ }
+ else if (p1.empty())
+ {
+ syntax_error(Config.s_MemoServ, u, "SET LIMIT", MEMO_SET_LIMIT_SERVADMIN_SYNTAX);
+ return MOD_CONT;
+ }
+ if ((!isdigit(p1[0]) && p1 != "NONE") || (!p2.empty() && p2 != "HARD"))
+ {
+ syntax_error(Config.s_MemoServ, u, "SET LIMIT", MEMO_SET_LIMIT_SERVADMIN_SYNTAX);
+ return MOD_CONT;
+ }
+ if (!chan.empty())
+ {
+ if (!p2.empty())
+ ci->SetFlag(CI_MEMO_HARDMAX);
+ else
+ ci->UnsetFlag(CI_MEMO_HARDMAX);
+ }
+ else
+ {
+ if (!p2.empty())
+ nc->SetFlag(NI_MEMO_HARDMAX);
+ else
+ nc->UnsetFlag(NI_MEMO_HARDMAX);
+ }
+ limit = atoi(p1.c_str());
+ if (limit < 0 || limit > 32767)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_LIMIT_OVERFLOW, 32767);
+ limit = 32767;
+ }
+ if (p1 == "NONE")
+ limit = -1;
+ }
+ else
+ {
+ if (p1.empty() || !p2.empty() || !isdigit(p1[0])) {
+ syntax_error(Config.s_MemoServ, u, "SET LIMIT", MEMO_SET_LIMIT_SYNTAX);
+ return MOD_CONT;
+ }
+ if (!chan.empty() && (ci->HasFlag(CI_MEMO_HARDMAX)))
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_LIMIT_FORBIDDEN, chan.c_str());
+ return MOD_CONT;
+ }
+ else if (chan.empty() && (nc->HasFlag(NI_MEMO_HARDMAX)))
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_YOUR_LIMIT_FORBIDDEN);
+ return MOD_CONT;
+ }
+ limit = atoi(p1.c_str());
+ /* The first character is a digit, but we could still go negative
+ * from overflow... watch out! */
+ if (limit < 0 || (Config.MSMaxMemos > 0 && limit > Config.MSMaxMemos))
+ {
+ if (!chan.empty())
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_LIMIT_TOO_HIGH, chan.c_str(), Config.MSMaxMemos);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_YOUR_LIMIT_TOO_HIGH, Config.MSMaxMemos);
+ return MOD_CONT;
+ }
+ else if (limit > 32767)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_LIMIT_OVERFLOW, 32767);
+ limit = 32767;
+ }
+ }
+ mi->memomax = limit;
+ if (limit > 0)
+ {
+ if (chan.empty() && nc == u->Account())
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_YOUR_LIMIT, limit);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_LIMIT, !chan.empty() ? chan.c_str() : user.c_str(), limit);
+ }
+ else if (!limit)
+ {
+ if (chan.empty() && nc == u->Account())
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_YOUR_LIMIT_ZERO);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_LIMIT_ZERO, !chan.empty() ? chan.c_str() : user.c_str());
+ }
+ else
+ {
+ if (chan.empty() && nc == u->Account())
+ notice_lang(Config.s_MemoServ, u, MEMO_UNSET_YOUR_LIMIT);
+ else
+ notice_lang(Config.s_MemoServ, u, MEMO_UNSET_LIMIT, !chan.empty() ? chan.c_str() : user.c_str());
+ }
+ return MOD_CONT;
+ }
+ public:
+ CommandMSSet() : Command("SET", 2, 5)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+ MemoInfo *mi = &u->Account()->memos;
+
+ if (readonly)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_DISABLED);
+ return MOD_CONT;
+ }
+ else if (cmd == "NOTIFY")
+ return this->DoNotify(u, params, mi);
+ else if (cmd == "LIMIT")
+ return this->DoLimit(u, params, mi);
+ else
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SET_UNKNOWN_OPTION, cmd.c_str());
+ notice_lang(Config.s_MemoServ, u, MORE_INFO, Config.s_MemoServ, "SET");
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_SET);
+ else if (subcommand == "NOTIFY")
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_SET_NOTIFY);
+ else if (subcommand == "LIMIT")
+ {
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_MemoServ, u, MEMO_SERVADMIN_HELP_SET_LIMIT, Config.MSMaxMemos);
+ else
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_SET_LIMIT, Config.MSMaxMemos);
+ }
+ else
+ return false;
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "SET", MEMO_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_SET);
+ }
+};
+
+class MSSet : public Module
+{
+ public:
+ MSSet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSSet());
+ }
+};
+
+MODULE_INIT(MSSet)
diff --git a/modules/core/ms_staff.cpp b/modules/core/ms_staff.cpp
new file mode 100644
index 000000000..481ad9db4
--- /dev/null
+++ b/modules/core/ms_staff.cpp
@@ -0,0 +1,74 @@
+/* MemoServ 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 CommandMSStaff : public Command
+{
+ public:
+ CommandMSStaff() : Command("STAFF", 1, 1, "memoserv/staff")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ int z = 0;
+ const char *text = params[0].c_str();
+
+ if (readonly)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_SEND_DISABLED);
+ return MOD_CONT;
+ }
+
+ for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it)
+ {
+ NickCore *nc = it->second;
+
+ if (nc->IsServicesOper())
+ memo_send(u, nc->display, text, z);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_MemoServ, u, MEMO_HELP_STAFF);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_MemoServ, u, "STAFF", MEMO_STAFF_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_MemoServ, u, MEMO_HELP_CMD_STAFF);
+ }
+};
+
+class MSStaff : public Module
+{
+ public:
+ MSStaff(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(MemoServ, new CommandMSStaff());
+ }
+};
+
+MODULE_INIT(MSStaff)
diff --git a/modules/core/ns_access.cpp b/modules/core/ns_access.cpp
new file mode 100644
index 000000000..4fc76903d
--- /dev/null
+++ b/modules/core/ns_access.cpp
@@ -0,0 +1,181 @@
+/* NickServ 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 CommandNSAccess : public Command
+{
+ private:
+ CommandReturn DoServAdminList(User *u, const std::vector<ci::string> &params, NickCore *nc)
+ {
+ const char *mask = params.size() > 2 ? params[2].c_str() : NULL;
+ unsigned i, end;
+
+ if (nc->access.empty())
+ {
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_LIST_X_EMPTY, nc->display);
+ return MOD_CONT;
+ }
+
+ if (nc->HasFlag(NI_SUSPENDED))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, nc->display);
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_LIST_X, params[1].c_str());
+ for (i = 0, end = nc->access.size(); i < end; ++i)
+ {
+ std::string access = nc->GetAccess(i);
+ if (mask && !Anope::Match(access, mask, true))
+ continue;
+ u->SendMessage(Config.s_NickServ, " %s", access.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoAdd(User *u, NickCore *nc, const char *mask)
+ {
+ if (!mask)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+
+ if (nc->access.size() >= Config.NSAccessMax)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_REACHED_LIMIT, Config.NSAccessMax);
+ return MOD_CONT;
+ }
+
+ if (nc->FindAccess(mask))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_ALREADY_PRESENT, *access);
+ return MOD_CONT;
+ }
+
+ nc->AddAccess(mask);
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_ADDED, mask);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, NickCore *nc, const char *mask)
+ {
+ if (!mask)
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (!nc->FindAccess(mask))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_NOT_FOUND, mask);
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_DELETED, mask);
+ nc->EraseAccess(mask);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, NickCore *nc, const char *mask)
+ {
+ unsigned i, end;
+
+ if (nc->access.empty())
+ {
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_LIST_EMPTY, u->nick.c_str());
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_NickServ, u, NICK_ACCESS_LIST);
+ for (i = 0, end = nc->access.size(); i < end; ++i)
+ {
+ std::string access = nc->GetAccess(i);
+ if (mask && !Anope::Match(access, mask, true))
+ continue;
+ u->SendMessage(Config.s_NickServ, " %s", access.c_str());
+ }
+
+ return MOD_CONT;
+ }
+ public:
+ CommandNSAccess() : Command("ACCESS", 1, 3)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+ const char *mask = params.size() > 1 ? params[1].c_str() : NULL;
+ NickAlias *na;
+
+ if (cmd == "LIST" && u->Account()->IsServicesOper() && mask && (na = findnick(params[1])))
+ return this->DoServAdminList(u, params, na->nc);
+
+ if (mask && !strchr(mask, '@'))
+ {
+ notice_lang(Config.s_NickServ, u, BAD_USERHOST_MASK);
+ notice_lang(Config.s_NickServ, u, MORE_INFO, Config.s_NickServ, "ACCESS");
+
+ }
+ /*
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ */
+ else if (u->Account()->HasFlag(NI_SUSPENDED))
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, u->Account()->display);
+ else if (cmd == "ADD")
+ return this->DoAdd(u, u->Account(), mask);
+ else if (cmd == "DEL")
+ return this->DoDel(u, u->Account(), mask);
+ else if (cmd == "LIST")
+ return this->DoList(u, u->Account(), mask);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_ACCESS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "ACCESS", NICK_ACCESS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_ACCESS);
+ }
+};
+
+class NSAccess : public Module
+{
+ public:
+ NSAccess(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSAccess());
+ }
+};
+
+MODULE_INIT(NSAccess)
diff --git a/modules/core/ns_alist.cpp b/modules/core/ns_alist.cpp
new file mode 100644
index 000000000..ebff3f0c8
--- /dev/null
+++ b/modules/core/ns_alist.cpp
@@ -0,0 +1,155 @@
+/* NickServ 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 CommandNSAList : public Command
+{
+ public:
+ CommandNSAList() : Command("ALIST", 0, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ /*
+ * List the channels that the given nickname has access on
+ *
+ * /ns ALIST [level]
+ * /ns ALIST [nickname] [level]
+ *
+ * -jester
+ */
+
+ const char *nick = NULL;
+
+ NickAlias *na;
+
+ int min_level = 0;
+ int is_servadmin = u->Account()->IsServicesOper();
+ unsigned lev_param = 0;
+
+ if (!is_servadmin)
+ /* Non service admins can only see their own levels */
+ na = findnick(u->Account()->display);
+ else
+ {
+ /* Services admins can request ALIST on nicks.
+ * The first argument for service admins must
+ * always be a nickname.
+ */
+ nick = params.size() ? params[0].c_str() : NULL;
+ lev_param = 1;
+
+ /* If an argument was passed, use it as the nick to see levels
+ * for, else check levels for the user calling the command */
+ if (nick)
+ na = findnick(nick);
+ else
+ na = findnick(u->nick);
+ }
+
+ /* If available, get level from arguments */
+ ci::string lev = params.size() > lev_param ? params[lev_param] : "";
+
+ /* if a level was given, make sure it's an int for later */
+ if (!lev.empty())
+ {
+ if (lev == "FOUNDER")
+ min_level = ACCESS_FOUNDER;
+ else if (lev == "SOP")
+ min_level = ACCESS_SOP;
+ else if (lev == "AOP")
+ min_level = ACCESS_AOP;
+ else if (lev == "HOP")
+ min_level = ACCESS_HOP;
+ else if (lev == "VOP")
+ min_level = ACCESS_VOP;
+ else
+ min_level = atoi(lev.c_str());
+ }
+
+ if (!na)
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else if (min_level <= ACCESS_INVALID || min_level > ACCESS_FOUNDER)
+ notice_lang(Config.s_NickServ, u, CHAN_ACCESS_LEVEL_RANGE, ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
+ else
+ {
+ int level;
+ int chan_count = 0;
+ int match_count = 0;
+
+ notice_lang(Config.s_NickServ, u, is_servadmin ? NICK_ALIST_HEADER_X : NICK_ALIST_HEADER, na->nick);
+
+ for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end; ++it)
+ {
+ ChannelInfo *ci = it->second;
+
+ if ((level = get_access_level(ci, na)))
+ {
+ ++chan_count;
+
+ if (min_level > level)
+ continue;
+
+ ++match_count;
+
+ if (ci->HasFlag(CI_XOP) || level == ACCESS_FOUNDER)
+ {
+ const char *xop;
+
+ xop = get_xop_level(level);
+
+ notice_lang(Config.s_NickServ, u, NICK_ALIST_XOP_FORMAT, match_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), xop, ci->desc ? ci->desc : "");
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_ALIST_ACCESS_FORMAT, match_count, ci->HasFlag(CI_NO_EXPIRE) ? '!' : ' ', ci->name.c_str(), level, ci->desc ? ci->desc : "");
+ }
+ }
+
+ notice_lang(Config.s_NickServ, u, NICK_ALIST_FOOTER, match_count, chan_count);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_ALIST);
+ else
+ notice_help(Config.s_NickServ, u, NICK_HELP_ALIST);
+
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_ALIST);
+ }
+};
+
+class NSAList : public Module
+{
+ public:
+ NSAList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSAList());
+ }
+};
+
+MODULE_INIT(NSAList)
diff --git a/modules/core/ns_drop.cpp b/modules/core/ns_drop.cpp
new file mode 100644
index 000000000..e18492bfd
--- /dev/null
+++ b/modules/core/ns_drop.cpp
@@ -0,0 +1,130 @@
+/* NickServ 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 CommandNSDrop : public Command
+{
+ public:
+ CommandNSDrop() : Command("DROP", 0, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params.size() ? params[0].c_str() : NULL;
+ NickAlias *na;
+ NickRequest *nr = NULL;
+ int is_mine; /* Does the nick being dropped belong to the user that is dropping? */
+ char *my_nick = NULL;
+
+ if (readonly)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_DROP_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (!(na = (nick ? findnick(nick) : findnick(u->nick))))
+ {
+ if (nick)
+ {
+ if ((nr = findrequestnick(nick)) && u->Account()->IsServicesOper())
+ {
+ if (Config.WallDrop)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used DROP on \2%s\2", u->nick.c_str(), nick);
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " dropped nickname " << nr->nick << " (e-mail: " << nr->email << ")";
+ delete nr;
+ notice_lang(Config.s_NickServ, u, NICK_X_DROPPED, nick);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_NOT_REGISTERED);
+ return MOD_CONT;
+ }
+
+ is_mine = u->Account() && u->Account() == na->nc;
+ if (is_mine && !nick)
+ my_nick = sstrdup(na->nick);
+
+ if (!is_mine && !u->Account()->HasPriv("nickserv/drop"))
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ else if (Config.NSSecureAdmins && !is_mine && na->nc->IsServicesOper())
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ else
+ {
+ if (readonly)
+ notice_lang(Config.s_NickServ, u, READ_ONLY_MODE);
+
+ if (ircd->sqline && (na->HasFlag(NS_FORBIDDEN)))
+ {
+ XLine x(na->nick);
+ ircdproto->SendSQLineDel(&x);
+ }
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " dropped nickname " << na->nick << " (group " << na->nc->display << ") (e-mail: " << (na->nc->email ? na->nc->email : "none") << ")";
+ delete na;
+
+ FOREACH_MOD(I_OnNickDrop, OnNickDrop(my_nick ? my_nick : nick));
+
+ if (!is_mine)
+ {
+ if (Config.WallDrop)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used DROP on \2%s\2", u->nick.c_str(), nick);
+ notice_lang(Config.s_NickServ, u, NICK_X_DROPPED, nick);
+ }
+ else
+ {
+ if (nick)
+ notice_lang(Config.s_NickServ, u, NICK_X_DROPPED, nick);
+ else
+ notice_lang(Config.s_NickServ, u, NICK_DROPPED);
+ }
+ }
+
+ if (my_nick)
+ delete [] my_nick;
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (u->Account() && u->Account()->HasPriv("nickserv/drop"))
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_DROP);
+ else
+ notice_help(Config.s_NickServ, u, NICK_HELP_DROP);
+
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_DROP);
+ }
+};
+
+class NSDrop : public Module
+{
+ public:
+ NSDrop(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSDrop());
+ }
+};
+
+MODULE_INIT(NSDrop)
diff --git a/modules/core/ns_forbid.cpp b/modules/core/ns_forbid.cpp
new file mode 100644
index 000000000..6f05dcdfd
--- /dev/null
+++ b/modules/core/ns_forbid.cpp
@@ -0,0 +1,122 @@
+/* NickServ 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 CommandNSForbid : public Command
+{
+ public:
+ CommandNSForbid() : Command("FORBID", 1, 2, "nickserv/forbid")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na;
+ const char *nick = params[0].c_str();
+ const char *reason = params.size() > 1 ? params[1].c_str() : NULL;
+
+ /* Assumes that permission checking has already been done. */
+ if (Config.ForceForbidReason && !reason)
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ notice_lang(Config.s_NickServ, u, READ_ONLY_MODE);
+ if (!ircdproto->IsNickValid(nick))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, nick);
+ return MOD_CONT;
+ }
+ if ((na = findnick(nick)))
+ {
+ if (Config.NSSecureAdmins && na->nc->IsServicesOper())
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ delete na;
+ }
+ NickCore *nc = new NickCore(nick);
+ nc->SetFlag(NI_FORBIDDEN);
+ na = new NickAlias(nick, nc);
+ if (na)
+ {
+ na->SetFlag(NS_FORBIDDEN);
+ na->last_usermask = sstrdup(u->nick.c_str());
+ if (reason)
+ na->last_realname = sstrdup(reason);
+
+ User *curr = finduser(na->nick);
+
+ if (curr)
+ {
+ notice_lang(Config.s_NickServ, curr, FORCENICKCHANGE_NOW);
+ curr->Collide(na);
+ }
+
+
+ if (ircd->sqline)
+ {
+ XLine x(na->nick, reason ? reason : "Forbidden");
+ ircdproto->SendSQLine(&x);
+ }
+
+ if (Config.WallForbid)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used FORBID on \2%s\2", u->nick.c_str(), nick);
+
+ Alog() << Config.s_NickServ << ": " << u->nick << " set FORBID for nick " << nick;
+ notice_lang(Config.s_NickServ, u, NICK_FORBID_SUCCEEDED, nick);
+
+ FOREACH_MOD(I_OnNickForbidden, OnNickForbidden(na));
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": Valid FORBID for " << nick << " by " << u->nick << " failed";
+ notice_lang(Config.s_NickServ, u, NICK_FORBID_FAILED, nick);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_FORBID);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "FORBID", Config.ForceForbidReason ? NICK_FORBID_SYNTAX_REASON : NICK_FORBID_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_FORBID);
+ }
+};
+
+class NSForbid : public Module
+{
+ public:
+ NSForbid(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSForbid());
+ }
+};
+
+MODULE_INIT(NSForbid)
diff --git a/modules/core/ns_getemail.cpp b/modules/core/ns_getemail.cpp
new file mode 100644
index 000000000..94653b856
--- /dev/null
+++ b/modules/core/ns_getemail.cpp
@@ -0,0 +1,83 @@
+/* NickServ 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.
+ *
+ * A simple call to check for all emails that a user may have registered
+ * with. It returns the nicks that match the email you provide. Wild
+ * Cards are not excepted. Must use user@email-host.
+ */
+
+/*************************************************************************/
+
+#include "module.h"
+
+class CommandNSGetEMail : public Command
+{
+ public:
+ CommandNSGetEMail() : Command("GETEMAIL", 1, 1, "nickserv/getemail")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string email = params[0];
+ int j = 0;
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used GETEMAIL on " << email;
+
+ for (nickcore_map::const_iterator it = NickCoreList.begin(), it_end = NickCoreList.end(); it != it_end; ++it)
+ {
+ NickCore *nc = it->second;
+
+ if (nc->email && nc->email == email)
+ {
+ ++j;
+ notice_lang(Config.s_NickServ, u, NICK_GETEMAIL_EMAILS_ARE, nc->display, email.c_str());
+ }
+ }
+
+ if (j <= 0)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_GETEMAIL_NOT_USED, email.c_str());
+ return MOD_CONT;
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_GETEMAIL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "GETMAIL", NICK_GETEMAIL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_GETEMAIL);
+ }
+};
+
+class NSGetEMail : public Module
+{
+ public:
+ NSGetEMail(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSGetEMail());
+ }
+};
+
+MODULE_INIT(NSGetEMail)
diff --git a/modules/core/ns_getpass.cpp b/modules/core/ns_getpass.cpp
new file mode 100644
index 000000000..e6f6533a3
--- /dev/null
+++ b/modules/core/ns_getpass.cpp
@@ -0,0 +1,94 @@
+/* NickServ 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 CommandNSGetPass : public Command
+{
+ public:
+ CommandNSGetPass() : Command("GETPASS", 1, 1, "nickserv/getpass")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ std::string tmp_pass;
+ NickAlias *na;
+ NickRequest *nr = NULL;
+
+ if (!(na = findnick(nick)))
+ {
+ if ((nr = findrequestnick(nick)))
+ {
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used GETPASS on " << nick;
+ if (Config.WallGetpass)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used GETPASS on \2%s\2", u->nick.c_str(), nick);
+ notice_lang(Config.s_NickServ, u, NICK_GETPASS_PASSCODE_IS, nick, nr->passcode.c_str());
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else if (Config.NSSecureAdmins && na->nc->IsServicesOper())
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ else
+ {
+ if (enc_decrypt(na->nc->pass, tmp_pass) == 1)
+ {
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used GETPASS on " << nick;
+ if (Config.WallGetpass)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used GETPASS on \2%s\2", u->nick.c_str(), nick);
+ notice_lang(Config.s_NickServ, u, NICK_GETPASS_PASSWORD_IS, nick, tmp_pass.c_str());
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_GETPASS_UNAVAILABLE);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_GETPASS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "GETPASS", NICK_GETPASS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_GETPASS);
+ }
+};
+
+class NSGetPass : public Module
+{
+ public:
+ NSGetPass(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ std::string tmp_pass = "plain:tmp";
+ if (enc_decrypt(tmp_pass, tmp_pass) == -1)
+ throw ModuleException("Incompatible with the encryption module being used");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSGetPass());
+ }
+};
+
+MODULE_INIT(NSGetPass)
diff --git a/modules/core/ns_ghost.cpp b/modules/core/ns_ghost.cpp
new file mode 100644
index 000000000..44e71c0c1
--- /dev/null
+++ b/modules/core/ns_ghost.cpp
@@ -0,0 +1,103 @@
+/* NickServ 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 CommandNSGhost : public Command
+{
+ public:
+ CommandNSGhost() : Command("GHOST", 1, 2)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ std::string pass = params.size() > 1 ? params[1].c_str() : "";
+ NickAlias *na = findnick(nick);
+
+ if (!finduser(nick))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_IN_USE, nick);
+ else if (!na)
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else if (na->nc->HasFlag(NI_SUSPENDED))
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, na->nick);
+ else if (!stricmp(nick, u->nick.c_str()))
+ notice_lang(Config.s_NickServ, u, NICK_NO_GHOST_SELF);
+ else if (!pass.empty())
+ {
+ int res = enc_check_password(pass, na->nc->pass);
+ if (res == 1)
+ {
+ std::string buf = "GHOST command used by " + u->nick;
+ kill_user(Config.s_NickServ, nick, buf.c_str());
+ notice_lang(Config.s_NickServ, u, NICK_GHOST_KILLED, nick);
+ }
+ else
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ if (!res)
+ {
+ Alog() << Config.s_NickServ << ": GHOST: invalid password for " << nick << " by " << u->GetMask();
+ if (bad_password(u))
+ return MOD_STOP;
+ }
+ }
+ }
+ else
+ {
+ if (u->Account() == na->nc || (!(na->nc->HasFlag(NI_SECURE)) && is_on_access(u, na->nc)))
+ {
+ std::string buf = "GHOST command used by " + u->nick;
+ kill_user(Config.s_NickServ, nick, buf.c_str());
+ notice_lang(Config.s_NickServ, u, NICK_GHOST_KILLED, nick);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_GHOST);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "GHOST", NICK_GHOST_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_GHOST);
+ }
+};
+
+class NSGhost : public Module
+{
+ public:
+ NSGhost(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSGhost());
+ }
+};
+
+MODULE_INIT(NSGhost)
diff --git a/modules/core/ns_group.cpp b/modules/core/ns_group.cpp
new file mode 100644
index 000000000..6b3e56718
--- /dev/null
+++ b/modules/core/ns_group.cpp
@@ -0,0 +1,287 @@
+/* NickServ 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 CommandNSGroup : public Command
+{
+ public:
+ CommandNSGroup() : Command("GROUP", 2, 2)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na, *target;
+ const char *nick = params[0].c_str();
+ std::string pass = params[1].c_str();
+ std::list<std::pair<ci::string, ci::string> >::iterator it, it_end;
+
+ if (Config.NSEmailReg && findrequestnick(u->nick))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_REQUESTED);
+ return MOD_CONT;
+ }
+
+ if (readonly)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_GROUP_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (!ircdproto->IsNickValid(u->nick.c_str()))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, u->nick.c_str());
+ return MOD_CONT;
+ }
+
+ if (Config.RestrictOperNicks)
+ for (it = Config.Opers.begin(), it_end = Config.Opers.end(); it != it_end; ++it)
+ if (!is_oper(u) && u->nick.find(it->first.c_str()) != std::string::npos)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
+ return MOD_CONT;
+ }
+
+ na = findnick(u->nick);
+ if (!(target = findnick(nick)))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ else if (time(NULL) < u->lastnickreg + Config.NSRegDelay)
+ notice_lang(Config.s_NickServ, u, NICK_GROUP_PLEASE_WAIT, (Config.NSRegDelay + u->lastnickreg) - time(NULL));
+ else if (u->Account() && u->Account()->HasFlag(NI_SUSPENDED))
+ {
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " tried to use GROUP from SUSPENDED nick " << target->nick;
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, u->nick.c_str());
+ }
+ else if (target && target->nc->HasFlag(NI_SUSPENDED))
+ {
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " tried to use GROUP for SUSPENDED nick " << target->nick;
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, target->nick);
+ }
+ else if (target->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, nick);
+ else if (na && target->nc == na->nc)
+ notice_lang(Config.s_NickServ, u, NICK_GROUP_SAME, target->nick);
+ else if (na && na->nc != u->Account())
+ notice_lang(Config.s_NickServ, u, NICK_IDENTIFY_REQUIRED, Config.s_NickServ);
+ else if (Config.NSMaxAliases && (target->nc->aliases.size() >= Config.NSMaxAliases) && !target->nc->IsServicesOper())
+ notice_lang(Config.s_NickServ, u, NICK_GROUP_TOO_MANY, target->nick, Config.s_NickServ, Config.s_NickServ);
+ else if (enc_check_password(pass, target->nc->pass) != 1)
+ {
+ Alog() << Config.s_NickServ << ": Failed GROUP for " << u->GetMask() << " (invalid password)";
+ notice_lang(Config.s_NickServ, u, PASSWORD_INCORRECT);
+ if (bad_password(u))
+ return MOD_STOP;
+ }
+ else
+ {
+ /* If the nick is already registered, drop it.
+ * If not, check that it is valid.
+ */
+ if (na)
+ delete na;
+ else
+ {
+ int prefixlen = strlen(Config.NSGuestNickPrefix);
+ int nicklen = u->nick.length();
+
+ if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && stristr(u->nick.c_str(), Config.NSGuestNickPrefix) == u->nick.c_str() && strspn(u->nick.c_str() + prefixlen, "1234567890") == nicklen - prefixlen)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
+ return MOD_CONT;
+ }
+ }
+
+ na = new NickAlias(u->nick, target->nc);
+
+ if (na)
+ {
+ std::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
+ na->last_usermask = sstrdup(last_usermask.c_str());
+ na->last_realname = sstrdup(u->realname);
+ na->time_registered = na->last_seen = time(NULL);
+
+ u->Login(na->nc);
+ FOREACH_MOD(I_OnNickGroup, OnNickGroup(u, target));
+ ircdproto->SetAutoIdentificationToken(u);
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " makes " << u->nick << " join group of " << target->nick << " (" << target->nc->display << ") (e-mail: " << (target->nc->email ? target->nc->email : "none") << ")";
+ notice_lang(Config.s_NickServ, u, NICK_GROUP_JOINED, target->nick);
+
+ u->lastnickreg = time(NULL);
+
+ check_memos(u);
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": makealias(" << u->nick << ") failed";
+ notice_lang(Config.s_NickServ, u, NICK_GROUP_FAILED);
+ }
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_GROUP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "GROUP", NICK_GROUP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_GROUP);
+ }
+};
+
+class CommandNSUngroup : public Command
+{
+ public:
+ CommandNSUngroup() : Command("UNGROUP", 0, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params.size() ? params[0].c_str() : NULL;
+ NickAlias *na = nick ? findnick(nick) : findnick(u->nick);
+
+ if (u->Account()->aliases.size() == 1)
+ notice_lang(Config.s_NickServ, u, NICK_UNGROUP_ONE_NICK);
+ else if (!na)
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick ? nick : u->nick.c_str());
+ else if (na->nc != u->Account())
+ notice_lang(Config.s_NickServ, u, NICK_UNGROUP_NOT_IN_GROUP, na->nick);
+ else
+ {
+ NickCore *oldcore = na->nc;
+
+ std::list<NickAlias *>::iterator it = std::find(oldcore->aliases.begin(), oldcore->aliases.end(), na);
+ if (it != oldcore->aliases.end())
+ oldcore->aliases.erase(it);
+
+ if (!stricmp(oldcore->display, na->nick))
+ change_core_display(oldcore);
+
+ na->nc = new NickCore(na->nick);
+ na->nc->aliases.push_back(na);
+
+ na->nc->pass = oldcore->pass;
+ if (oldcore->email)
+ na->nc->email = sstrdup(oldcore->email);
+ if (oldcore->greet)
+ na->nc->greet = sstrdup(oldcore->greet);
+ na->nc->language = oldcore->language;
+
+ notice_lang(Config.s_NickServ, u, NICK_UNGROUP_SUCCESSFUL, na->nick, oldcore->display);
+
+ User *user = finduser(na->nick);
+ if (user)
+ /* The user on the nick who was ungrouped may be identified to the old group, set -r */
+ user->RemoveMode(NickServ, UMODE_REGISTERED);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_UNGROUP);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_UNGROUP);
+ }
+};
+
+class CommandNSGList : public Command
+{
+ public:
+ CommandNSGList() : Command("GLIST", 0, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params.size() ? params[0].c_str() : NULL;
+
+ NickCore *nc = u->Account();
+
+ if (nick && (stricmp(nick, u->nick.c_str()) && !u->Account()->IsServicesOper()))
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED, Config.s_NickServ);
+ else if (nick && (!findnick(nick) || !(nc = findnick(nick)->nc)))
+ notice_lang(Config.s_NickServ, u, !nick ? NICK_NOT_REGISTERED : NICK_X_NOT_REGISTERED, nick);
+ else
+ {
+ time_t expt;
+ struct tm *tm;
+ char buf[BUFSIZE];
+ int wont_expire;
+
+ notice_lang(Config.s_NickServ, u, nick ? NICK_GLIST_HEADER_X : NICK_GLIST_HEADER, nc->display);
+ for (std::list<NickAlias *>::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it)
+ {
+ NickAlias *na2 = *it;
+
+ if (!(wont_expire = na2->HasFlag(NS_NO_EXPIRE)))
+ {
+ expt = na2->last_seen + Config.NSExpire;
+ tm = localtime(&expt);
+ strftime_lang(buf, sizeof(buf), finduser(na2->nick), STRFTIME_DATE_TIME_FORMAT, tm);
+ }
+ notice_lang(Config.s_NickServ, u, wont_expire ? NICK_GLIST_REPLY_NOEXPIRE : NICK_GLIST_REPLY, na2->nick, buf);
+ }
+ notice_lang(Config.s_NickServ, u, NICK_GLIST_FOOTER, nc->aliases.size());
+ }
+ return MOD_CONT;
+ }
+
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_GLIST);
+ else
+ notice_help(Config.s_NickServ, u, NICK_HELP_GLIST);
+
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_GLIST);
+ }
+};
+
+class NSGroup : public Module
+{
+ public:
+ NSGroup(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSGroup());
+ this->AddCommand(NickServ, new CommandNSUngroup());
+ this->AddCommand(NickServ, new CommandNSGList());
+ }
+};
+
+MODULE_INIT(NSGroup)
diff --git a/modules/core/ns_help.cpp b/modules/core/ns_help.cpp
new file mode 100644
index 000000000..d23cbd208
--- /dev/null
+++ b/modules/core/ns_help.cpp
@@ -0,0 +1,67 @@
+/* NickServ 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 CommandNSHelp : public Command
+{
+ public:
+ CommandNSHelp() : Command("HELP", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (cmd == "SET LANGUAGE")
+ {
+ int i;
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_LANGUAGE);
+ for (i = 0; i < NUM_LANGS && langlist[i] >= 0; ++i)
+ u->SendMessage(Config.s_NickServ, " %2d) %s", i + 1, langnames[langlist[i]]);
+ }
+ else
+ mod_help_cmd(NickServ, u, cmd.c_str());
+
+ return MOD_CONT;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP);
+ for (CommandMap::const_iterator it = NickServ->Commands.begin(), it_end = NickServ->Commands.end(); it != it_end; ++it)
+ if (!Config.HidePrivilegedCommands || it->second->permission.empty() || (u->Account() && u->Account()->HasCommand(it->second->permission)))
+ it->second->OnServHelp(u);
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP);
+ if (Config.NSExpire >= 86400)
+ notice_help(Config.s_NickServ, u, NICK_HELP_EXPIRES, Config.NSExpire / 86400);
+ notice_help(Config.s_NickServ, u, NICK_HELP_FOOTER);
+ }
+};
+
+class NSHelp : public Module
+{
+ public:
+ NSHelp(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSHelp());
+ }
+};
+
+MODULE_INIT(NSHelp)
diff --git a/modules/core/ns_identify.cpp b/modules/core/ns_identify.cpp
new file mode 100644
index 000000000..51a0716a4
--- /dev/null
+++ b/modules/core/ns_identify.cpp
@@ -0,0 +1,125 @@
+/* NickServ 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 CommandNSIdentify : public Command
+{
+ public:
+ CommandNSIdentify(const ci::string &cname) : Command(cname, 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ std::string pass = params[0].c_str();
+ NickAlias *na;
+ NickRequest *nr;
+ int res;
+
+ if (!(na = findnick(u->nick)))
+ {
+ if ((nr = findrequestnick(u->nick)))
+ notice_lang(Config.s_NickServ, u, NICK_IS_PREREG);
+ else
+ notice_lang(Config.s_NickServ, u, NICK_NOT_REGISTERED);
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else if (na->nc->HasFlag(NI_SUSPENDED))
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, na->nick);
+ /* You can now identify for other nicks without logging out first,
+ * however you can not identify again for the group you're already
+ * identified as
+ */
+ else if (u->Account() && u->Account() == na->nc)
+ notice_lang(Config.s_NickServ, u, NICK_ALREADY_IDENTIFIED);
+ else if (!(res = enc_check_password(pass, na->nc->pass)))
+ {
+ Alog() << Config.s_NickServ << ": Failed IDENTIFY for " << u->nick << "!" << u->GetIdent() << "@" << u->host;
+ notice_lang(Config.s_NickServ, u, PASSWORD_INCORRECT);
+ if (bad_password(u))
+ return MOD_STOP;
+ }
+ else if (res == -1)
+ notice_lang(Config.s_NickServ, u, NICK_IDENTIFY_FAILED);
+ else
+ {
+ if (u->IsIdentified())
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " logged out of account " << u->Account()->display;
+
+ if (na->last_realname)
+ delete [] na->last_realname;
+ na->last_realname = sstrdup(u->realname);
+ na->last_seen = time(NULL);
+
+ u->Login(na->nc);
+ ircdproto->SendAccountLogin(u, u->Account());
+ ircdproto->SetAutoIdentificationToken(u);
+
+ u->UpdateHost();
+
+ FOREACH_MOD(I_OnNickIdentify, OnNickIdentify(u));
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " identified for account " << u->Account()->display;
+ notice_lang(Config.s_NickServ, u, NICK_IDENTIFY_SUCCEEDED);
+ if (ircd->vhost)
+ do_on_id(u);
+ if (Config.NSModeOnID)
+ do_setmodes(u);
+
+ if (Config.NSForceEmail && u->Account() && !u->Account()->email)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_IDENTIFY_EMAIL_REQUIRED);
+ notice_help(Config.s_NickServ, u, NICK_IDENTIFY_EMAIL_HOWTO);
+ }
+
+ if (u->IsIdentified())
+ check_memos(u);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_IDENTIFY);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "IDENTIFY", NICK_IDENTIFY_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ if (this->name == "IDENTIFY")
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_IDENTIFY);
+ }
+};
+
+class NSIdentify : public Module
+{
+ public:
+ NSIdentify(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSIdentify("IDENTIFY"));
+ this->AddCommand(NickServ, new CommandNSIdentify("ID"));
+ }
+};
+
+MODULE_INIT(NSIdentify)
diff --git a/modules/core/ns_info.cpp b/modules/core/ns_info.cpp
new file mode 100644
index 000000000..3109ad4ba
--- /dev/null
+++ b/modules/core/ns_info.cpp
@@ -0,0 +1,191 @@
+/* NickServ 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 CommandNSInfo : public Command
+{
+ private:
+ // cannot be const, as it is modified
+ void CheckOptStr(std::string &buf, NickCoreFlag opt, const std::string &str, NickCore *nc, bool reverse_logic = false)
+ {
+ if (reverse_logic ? !nc->HasFlag(opt) : nc->HasFlag(opt))
+ {
+ const char *commastr = getstring(nc, COMMA_SPACE);
+ if (!buf.empty())
+ buf += commastr;
+
+ buf += str;
+ }
+ }
+ public:
+ CommandNSInfo() : Command("INFO", 1, 1)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+
+ NickAlias *na = findnick(nick);
+ bool has_auspex = u->IsIdentified() && u->Account()->HasPriv("nickserv/auspex");
+
+ if (!na)
+ {
+ NickRequest *nr = findrequestnick(nick);
+ if (nr)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_IS_PREREG);
+ if (has_auspex)
+ notice_lang(Config.s_NickServ, u, NICK_INFO_EMAIL, nr->email);
+ }
+ else if (nickIsServices(nick, 1))
+ notice_lang(Config.s_NickServ, u, NICK_X_IS_SERVICES, nick);
+ else
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ }
+ else if (na->HasFlag(NS_FORBIDDEN))
+ {
+ if (is_oper(u) && na->last_usermask)
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN_OPER, nick, na->last_usermask, na->last_realname ? na->last_realname : getstring(u, NO_REASON));
+ else
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, nick);
+ }
+ else
+ {
+ struct tm *tm;
+ char buf[BUFSIZE];
+ bool nick_online = false, show_hidden = false;
+ time_t expt;
+
+ /* Is the real owner of the nick we're looking up online? -TheShadow */
+ User *u2 = finduser(na->nick);
+ if (u2 && u2->Account() == na->nc)
+ nick_online = true;
+
+ if (has_auspex || (u->Account() && na->nc == u->Account()))
+ show_hidden = true;
+
+ notice_lang(Config.s_NickServ, u, NICK_INFO_REALNAME, na->nick, na->last_realname);
+
+ if (na->nc->IsServicesOper() && (show_hidden || !na->nc->HasFlag(NI_HIDE_STATUS)))
+ notice_lang(Config.s_NickServ, u, NICK_INFO_SERVICES_OPERTYPE, na->nick, na->nc->ot->GetName().c_str());
+
+ if (nick_online)
+ {
+ if (show_hidden || !(na->nc->HasFlag(NI_HIDE_MASK)))
+ notice_lang(Config.s_NickServ, u, NICK_INFO_ADDRESS_ONLINE, na->last_usermask);
+ else
+ notice_lang(Config.s_NickServ, u, NICK_INFO_ADDRESS_ONLINE_NOHOST, na->nick);
+ }
+ else
+ {
+ if (show_hidden || !(na->nc->HasFlag(NI_HIDE_MASK)))
+ notice_lang(Config.s_NickServ, u, NICK_INFO_ADDRESS, na->last_usermask);
+ }
+
+ tm = localtime(&na->time_registered);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_NickServ, u, NICK_INFO_TIME_REGGED, buf);
+
+ if (!nick_online)
+ {
+ tm = localtime(&na->last_seen);
+ strftime_lang(buf, sizeof(buf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_NickServ, u, NICK_INFO_LAST_SEEN, buf);
+ }
+
+ if (na->last_quit && (show_hidden || !(na->nc->HasFlag(NI_HIDE_QUIT))))
+ notice_lang(Config.s_NickServ, u, NICK_INFO_LAST_QUIT, na->last_quit);
+
+ if (na->nc->email && (show_hidden || !(na->nc->HasFlag(NI_HIDE_EMAIL))))
+ notice_lang(Config.s_NickServ, u, NICK_INFO_EMAIL, na->nc->email);
+
+ if (show_hidden)
+ {
+ if (Config.s_HostServ && ircd->vhost && na->hostinfo.HasVhost())
+ {
+ if (ircd->vident && !na->hostinfo.GetIdent().empty())
+ notice_lang(Config.s_NickServ, u, NICK_INFO_VHOST2, na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str());
+ else
+ notice_lang(Config.s_NickServ, u, NICK_INFO_VHOST, na->hostinfo.GetHost().c_str());
+ }
+ if (na->nc->greet)
+ notice_lang(Config.s_NickServ, u, NICK_INFO_GREET, na->nc->greet);
+
+ std::string optbuf;
+
+ CheckOptStr(optbuf, NI_KILLPROTECT, getstring(u, NICK_INFO_OPT_KILL), na->nc);
+ CheckOptStr(optbuf, NI_SECURE, getstring(u, NICK_INFO_OPT_SECURE), na->nc);
+ CheckOptStr(optbuf, NI_PRIVATE, getstring(u, NICK_INFO_OPT_PRIVATE), na->nc);
+ CheckOptStr(optbuf, NI_MSG, getstring(u, NICK_INFO_OPT_MSG), na->nc);
+ CheckOptStr(optbuf, NI_AUTOOP, getstring(u, NICK_INFO_OPT_AUTOOP), na->nc);
+
+ notice_lang(Config.s_NickServ, u, NICK_INFO_OPTIONS, optbuf.empty() ? getstring(u, NICK_INFO_OPT_NONE) : optbuf.c_str());
+
+ if (na->nc->HasFlag(NI_SUSPENDED))
+ {
+ if (na->last_quit)
+ notice_lang(Config.s_NickServ, u, NICK_INFO_SUSPENDED, na->last_quit);
+ else
+ notice_lang(Config.s_NickServ, u, NICK_INFO_SUSPENDED_NO_REASON);
+ }
+
+ if (na->HasFlag(NS_NO_EXPIRE))
+ notice_lang(Config.s_NickServ, u, NICK_INFO_NO_EXPIRE);
+ else
+ {
+ expt = na->last_seen + Config.NSExpire;
+ tm = localtime(&expt);
+ strftime_lang(buf, sizeof(buf), finduser(na->nick), STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_NickServ, u, NICK_INFO_EXPIRE, buf);
+ }
+ }
+
+ FOREACH_MOD(I_OnNickInfo, OnNickInfo(u, na, show_hidden));
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_INFO);
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "INFO", NICK_INFO_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_INFO);
+ }
+};
+
+class NSInfo : public Module
+{
+ public:
+ NSInfo(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSInfo());
+ }
+};
+
+MODULE_INIT(NSInfo)
diff --git a/modules/core/ns_list.cpp b/modules/core/ns_list.cpp
new file mode 100644
index 000000000..d126bd104
--- /dev/null
+++ b/modules/core/ns_list.cpp
@@ -0,0 +1,219 @@
+/* NickServ 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"
+#include "hashcomp.h"
+
+class CommandNSList : public Command
+{
+ public:
+ CommandNSList() : Command("LIST", 1, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ /* SADMINS can search for nicks based on their NS_FORBIDDEN and NS_NO_EXPIRE
+ * status. The keywords FORBIDDEN and NOEXPIRE represent these two states
+ * respectively. These keywords should be included after the search pattern.
+ * Multiple keywords are accepted and should be separated by spaces. Only one
+ * of the keywords needs to match a nick's state for the nick to be displayed.
+ * Forbidden nicks can be identified by "[Forbidden]" appearing in the last
+ * seen address field. Nicks with NOEXPIRE set are preceeded by a "!". Only
+ * SADMINS will be shown forbidden nicks and the "!" indicator.
+ * Syntax for sadmins: LIST pattern [FORBIDDEN] [NOEXPIRE]
+ * -TheShadow
+ *
+ * UPDATE: SUSPENDED keyword is now accepted as well.
+ */
+ const char *pattern = params[0].c_str();
+ NickCore *mync;
+ unsigned nnicks;
+ char buf[BUFSIZE];
+ bool is_servadmin = u->Account()->IsServicesOper();
+ char noexpire_char = ' ';
+ int count = 0, from = 0, to = 0, tofree = 0;
+ char *tmp = NULL;
+ char *s = NULL;
+ bool suspended, nsnoexpire, forbidden, unconfirmed;
+
+ suspended = nsnoexpire = forbidden = unconfirmed = false;
+
+ if (Config.NSListOpersOnly && !is_oper(u)) /* reverse the help logic */
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ return MOD_STOP;
+ }
+
+ if (pattern[0] == '#')
+ {
+ tmp = myStrGetOnlyToken((pattern + 1), '-', 0); /* Read FROM out */
+ if (!tmp)
+ {
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; ++s)
+ if (!isdigit(*s))
+ {
+ delete [] tmp;
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ from = atoi(tmp);
+ delete [] tmp;
+ tmp = myStrGetTokenRemainder(pattern, '-', 1); /* Read TO out */
+ if (!tmp)
+ {
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ for (s = tmp; *s; ++s)
+ if (!isdigit(*s))
+ {
+ delete [] tmp;
+ notice_lang(Config.s_ChanServ, u, LIST_INCORRECT_RANGE);
+ return MOD_CONT;
+ }
+ to = atoi(tmp);
+ delete [] tmp;
+ pattern = sstrdup("*");
+ tofree = 1;
+ }
+
+ nnicks = 0;
+
+ if (is_servadmin && params.size() > 1)
+ {
+ ci::string keyword;
+ spacesepstream keywords(params[1].c_str());
+ while (keywords.GetToken(keyword))
+ {
+ if (keyword == "FORBIDDEN")
+ forbidden = true;
+ if (keyword == "NOEXPIRE")
+ nsnoexpire = true;
+ if (keyword == "SUSPENDED")
+ suspended = true;
+ if (keyword == "UNCONFIRMED")
+ unconfirmed = true;
+ }
+ }
+
+ mync = u->Account();
+
+ notice_lang(Config.s_NickServ, u, NICK_LIST_HEADER, pattern);
+ if (!unconfirmed)
+ {
+ for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ++it)
+ {
+ NickAlias *na = it->second;
+
+ /* Don't show private and forbidden nicks to non-services admins. */
+ if (na->HasFlag(NS_FORBIDDEN) && !is_servadmin)
+ continue;
+ if (na->nc->HasFlag(NI_PRIVATE) && !is_servadmin && na->nc != mync)
+ continue;
+ if (forbidden && !na->HasFlag(NS_FORBIDDEN))
+ continue;
+ else if (nsnoexpire && !na->HasFlag(NS_NO_EXPIRE))
+ continue;
+ else if (suspended && !na->nc->HasFlag(NI_SUSPENDED))
+ continue;
+
+ /* We no longer compare the pattern against the output buffer.
+ * Instead we build a nice nick!user@host buffer to compare.
+ * The output is then generated separately. -TheShadow */
+ snprintf(buf, sizeof(buf), "%s!%s", na->nick, na->last_usermask && !na->HasFlag(NS_FORBIDDEN) ? na->last_usermask : "*@*");
+ if (!stricmp(pattern, na->nick) || Anope::Match(buf, pattern, false))
+ {
+ if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= Config.NSListMax)
+ {
+ if (is_servadmin && (na->HasFlag(NS_NO_EXPIRE)))
+ noexpire_char = '!';
+ else
+ noexpire_char = ' ';
+ if ((na->nc->HasFlag(NI_HIDE_MASK)) && !is_servadmin && na->nc != mync)
+ snprintf(buf, sizeof(buf), "%-20s [Hostname Hidden]", na->nick);
+ else if (na->HasFlag(NS_FORBIDDEN))
+ snprintf(buf, sizeof(buf), "%-20s [Forbidden]", na->nick);
+ else if (na->nc->HasFlag(NI_SUSPENDED))
+ snprintf(buf, sizeof(buf), "%-20s [Suspended]", na->nick);
+ else
+ snprintf(buf, sizeof(buf), "%-20s %s", na->nick, na->last_usermask);
+ u->SendMessage(Config.s_NickServ, " %c%s", noexpire_char, buf);
+ }
+ ++count;
+ }
+ }
+ }
+
+ if (unconfirmed || is_servadmin)
+ {
+ noexpire_char = ' ';
+
+ for (nickrequest_map::const_iterator it = NickRequestList.begin(), it_end = NickRequestList.end(); it != it_end; ++it)
+ {
+ NickRequest *nr = it->second;
+
+ snprintf(buf, sizeof(buf), "%s!*@*", nr->nick);
+ if ((!stricmp(pattern, nr->nick) || Anope::Match(buf, pattern, false)) && ++nnicks <= Config.NSListMax)
+ {
+ snprintf(buf, sizeof(buf), "%-20s [UNCONFIRMED]", nr->nick);
+ u->SendMessage(Config.s_NickServ, " %c%s", noexpire_char, buf);
+ }
+ }
+ }
+ notice_lang(Config.s_NickServ, u, NICK_LIST_RESULTS, nnicks > Config.NSListMax ? Config.NSListMax : nnicks, nnicks);
+ if (tofree)
+ delete [] pattern;
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_LIST);
+ else
+ notice_help(Config.s_NickServ, u, NICK_HELP_LIST);
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ if (u->Account()->IsServicesOper())
+ syntax_error(Config.s_NickServ, u, "LIST", NICK_LIST_SERVADMIN_SYNTAX);
+ else
+ syntax_error(Config.s_NickServ, u, "LIST", NICK_LIST_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_LIST);
+ }
+};
+
+class NSList : public Module
+{
+ public:
+ NSList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSList());
+ }
+};
+
+MODULE_INIT(NSList)
diff --git a/modules/core/ns_logout.cpp b/modules/core/ns_logout.cpp
new file mode 100644
index 000000000..cf49674d0
--- /dev/null
+++ b/modules/core/ns_logout.cpp
@@ -0,0 +1,96 @@
+/* NickServ 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 CommandNSLogout : public Command
+{
+ public:
+ CommandNSLogout() : Command("LOGOUT", 0, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params.size() ? params[0].c_str() : NULL;
+ ci::string param = params.size() > 1 ? params[1] : "";
+ User *u2;
+ NickAlias *na;
+
+ if (!u->Account()->IsServicesOper() && nick)
+ this->OnSyntaxError(u, "");
+ else if (!(u2 = (nick ? finduser(nick) : u)))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_IN_USE, nick);
+ else if (nick && u2->Account() && !u2->Account()->IsServicesOper())
+ notice_lang(Config.s_NickServ, u, NICK_LOGOUT_SERVICESADMIN, nick);
+ else
+ {
+ na = findnick(u2->nick);
+
+ if (nick && !param.empty() && param == "REVALIDATE")
+ validate_user(u2);
+
+ u2->isSuperAdmin = 0; /* Dont let people logout and remain a SuperAdmin */
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " logged out nickname " << u2->nick;
+
+ /* Remove founder status from this user in all channels */
+ if (nick)
+ notice_lang(Config.s_NickServ, u, NICK_LOGOUT_X_SUCCEEDED, nick);
+ else
+ notice_lang(Config.s_NickServ, u, NICK_LOGOUT_SUCCEEDED);
+
+ ircdproto->SendAccountLogout(u2, u2->Account());
+ ircdproto->SendUnregisteredNick(u2);
+
+ u2->Logout();
+
+ /* Send out an event */
+ FOREACH_MOD(I_OnNickLogout, OnNickLogout(u2));
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (u->Account() && u->Account()->IsServicesOper())
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_LOGOUT);
+ else
+ notice_help(Config.s_NickServ, u, NICK_HELP_LOGOUT);
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "LOGOUT", NICK_LOGOUT_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_LOGOUT);
+ }
+};
+
+class NSLogout : public Module
+{
+ public:
+ NSLogout(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSLogout());
+ }
+};
+
+MODULE_INIT(NSLogout)
diff --git a/modules/core/ns_recover.cpp b/modules/core/ns_recover.cpp
new file mode 100644
index 000000000..acbc7073b
--- /dev/null
+++ b/modules/core/ns_recover.cpp
@@ -0,0 +1,124 @@
+/* NickServ 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 CommandNSRecover : public Command
+{
+ public:
+ CommandNSRecover() : Command("RECOVER", 1, 2)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ std::string pass = params.size() > 1 ? params[1].c_str() : "";
+ NickAlias *na;
+ User *u2;
+
+ if (!(u2 = finduser(nick)))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_IN_USE, nick);
+ else if (!(na = findnick(u2->nick)))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else if (na->nc->HasFlag(NI_SUSPENDED))
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, na->nick);
+ else if (!stricmp(nick, u->nick.c_str()))
+ notice_lang(Config.s_NickServ, u, NICK_NO_RECOVER_SELF);
+ else if (!pass.empty())
+ {
+ int res = enc_check_password(pass, na->nc->pass);
+
+ if (res == 1)
+ {
+ char relstr[192];
+
+ notice_lang(Config.s_NickServ, u2, FORCENICKCHANGE_NOW);
+ u2->Collide(na);
+
+ /* Convert Config.NSReleaseTimeout seconds to string format */
+ duration(na->nc, relstr, sizeof(relstr), Config.NSReleaseTimeout);
+
+ notice_lang(Config.s_NickServ, u, NICK_RECOVERED, Config.s_NickServ, nick, relstr);
+ }
+ else
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ if (!res)
+ {
+ Alog() << Config.s_NickServ << ": RECOVER: invalid password for " << nick << " by " << u->GetMask();
+ if (bad_password(u))
+ return MOD_STOP;
+ }
+ }
+ }
+ else
+ {
+ if (u->Account() == na->nc || (!na->nc->HasFlag(NI_SECURE) && is_on_access(u, na->nc)))
+ {
+ char relstr[192];
+
+ notice_lang(Config.s_NickServ, u2, FORCENICKCHANGE_NOW);
+ u2->Collide(na);
+
+ /* Convert Config.NSReleaseTimeout seconds to string format */
+ duration(na->nc, relstr, sizeof(relstr), Config.NSReleaseTimeout);
+
+ notice_lang(Config.s_NickServ, u, NICK_RECOVERED, Config.s_NickServ, nick, relstr);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ char relstr[192];
+
+ /* Convert Config.NSReleaseTimeout seconds to string format */
+ duration(u->Account(), relstr, sizeof(relstr), Config.NSReleaseTimeout);
+
+ notice_help(Config.s_NickServ, u, NICK_HELP_RECOVER, relstr);
+ //do_help_limited(Config.s_NickServ, u, this);
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "RECOVER", NICK_RECOVER_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_RECOVER);
+ }
+};
+
+class NSRecover : public Module
+{
+ public:
+ NSRecover(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSRecover());
+ }
+};
+
+MODULE_INIT(NSRecover)
diff --git a/modules/core/ns_register.cpp b/modules/core/ns_register.cpp
new file mode 100644
index 000000000..3fba0407e
--- /dev/null
+++ b/modules/core/ns_register.cpp
@@ -0,0 +1,395 @@
+/* NickServ 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"
+
+static bool SendRegmail(User *u, NickRequest *nr);
+
+class CommandNSConfirm : public Command
+{
+ protected:
+ CommandReturn ActuallyConfirmNick(User *u, NickRequest *nr, bool force)
+ {
+ NickAlias *na = new NickAlias(nr->nick, new NickCore(nr->nick));
+
+ if (!na)
+ {
+ Alog() << Config.s_NickServ << ": makenick(" << u->nick << ") failed";
+ notice_lang(Config.s_NickServ, u, NICK_REGISTRATION_FAILED);
+ return MOD_CONT;
+ }
+
+ std::string tmp_pass;
+
+ na->nc->pass = nr->password;
+
+ na->nc->memos.memomax = Config.MSMaxMemos;
+
+ if (force)
+ {
+ na->last_usermask = sstrdup("*@*");
+ na->last_realname = sstrdup("unknown");
+ }
+ else
+ {
+ std::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost();
+ na->last_usermask = sstrdup(last_usermask.c_str());
+ na->last_realname = sstrdup(u->realname);
+ if (Config.NSAddAccessOnReg)
+ na->nc->AddAccess(create_mask(u));
+ }
+
+ na->time_registered = na->last_seen = time(NULL);
+ na->nc->language = Config.NSDefLanguage;
+ if (nr->email)
+ na->nc->email = sstrdup(nr->email);
+
+ if (!force)
+ {
+ u->Login(na->nc);
+ Alog() << Config.s_NickServ << ": '" << u->nick << "' registered by " << u->GetIdent() << "@" << u->host << " (e-mail: " << (nr->email ? nr->email : "none") << ")";
+ if (Config.NSAddAccessOnReg)
+ notice_lang(Config.s_NickServ, u, NICK_REGISTERED, u->nick.c_str(), na->nc->GetAccess(0).c_str());
+ else
+ notice_lang(Config.s_NickServ, u, NICK_REGISTERED_NO_MASK, u->nick.c_str());
+ delete nr;
+
+ ircdproto->SendAccountLogin(u, u->Account());
+ ircdproto->SetAutoIdentificationToken(u);
+
+ if (enc_decrypt(na->nc->pass, tmp_pass) == 1)
+ notice_lang(Config.s_NickServ, u, NICK_PASSWORD_IS, tmp_pass.c_str());
+
+ u->lastnickreg = time(NULL);
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": '" << nr->nick << "' confirmed by " << u->GetMask() << " (email: " << (nr->email ? nr->email : "none") << " )";
+ notice_lang(Config.s_NickServ, u, NICK_FORCE_REG, nr->nick);
+ User *user = finduser(nr->nick);
+ /* Delrequest must be called before validate_user */
+ delete nr;
+ if (user)
+ validate_user(user);
+ }
+
+ FOREACH_MOD(I_OnNickRegister, OnNickRegister(na));
+
+ return MOD_CONT;
+
+ }
+
+ CommandReturn DoConfirm(User *u, const std::vector<ci::string> &params)
+ {
+ std::string passcode = !params.empty() ? params[0].c_str() : "";
+
+ NickRequest *nr = findrequestnick(u->nick);
+
+ if (Config.NSEmailReg)
+ {
+ if (passcode.empty())
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ if (!nr)
+ {
+ if (u->Account() && u->Account()->HasPriv("nickserv/confirm"))
+ {
+ /* If an admin, their nick is obviously already regged, so look at the passcode to get the nick
+ of the user they are trying to validate, and push that user through regardless of passcode */
+ nr = findrequestnick(passcode);
+ if (nr)
+ {
+ ActuallyConfirmNick(u, nr, true);
+ return MOD_CONT;
+ }
+ }
+ notice_lang(Config.s_NickServ, u, NICK_CONFIRM_NOT_FOUND, Config.s_NickServ);
+
+ return MOD_CONT;
+ }
+
+ if (nr->passcode.compare(passcode))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_CONFIRM_INVALID);
+ return MOD_CONT;
+ }
+ }
+
+ if (!nr)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_REGISTRATION_FAILED);
+ return MOD_CONT;
+ }
+
+ ActuallyConfirmNick(u, nr, false);
+ return MOD_CONT;
+ }
+
+ public:
+ CommandNSConfirm(const ci::string &cmdn, int min, int max) : Command(cmdn, min, max)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoConfirm(u, params);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_CONFIRM);
+ if (u->Account() && u->Account()->HasPriv("nickserv/confirm"))
+ notice_help(Config.s_NickServ, u, NICK_HELP_CONFIRM_OPER);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_CONFIRM_INVALID);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_CONFIRM);
+ }
+};
+
+class CommandNSRegister : public CommandNSConfirm
+{
+ public:
+ CommandNSRegister() : CommandNSConfirm("REGISTER", 1, 2)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickRequest *nr = NULL, *anr = NULL;
+ NickAlias *na;
+ int prefixlen = strlen(Config.NSGuestNickPrefix);
+ int nicklen = u->nick.length();
+ const char *pass = params[0].c_str();
+ const char *email = params.size() > 1 ? params[1].c_str() : NULL;
+ char passcode[11];
+ int idx, min = 1, max = 62;
+ int chars[] = {
+ ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
+ 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
+ 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
+ };
+ std::list<std::pair<ci::string, ci::string> >::iterator it, it_end;
+
+ if (readonly)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_REGISTRATION_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (!is_oper(u) && Config.NickRegDelay && time(NULL) - u->my_signon < Config.NickRegDelay)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_REG_DELAY, Config.NickRegDelay);
+ return MOD_CONT;
+ }
+
+ if ((anr = findrequestnick(u->nick)))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_REQUESTED);
+ return MOD_CONT;
+ }
+
+ /* Prevent "Guest" nicks from being registered. -TheShadow */
+
+ /* Guest nick can now have a series of between 1 and 7 digits.
+ * --lara
+ */
+ if (nicklen <= prefixlen + 7 && nicklen >= prefixlen + 1 && stristr(u->nick.c_str(), Config.NSGuestNickPrefix) == u->nick.c_str() && strspn(u->nick.c_str() + prefixlen, "1234567890") == nicklen - prefixlen)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
+ return MOD_CONT;
+ }
+
+ if (!ircdproto->IsNickValid(u->nick.c_str()))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, u->nick.c_str());
+ return MOD_CONT;
+ }
+
+ if (Config.RestrictOperNicks)
+ for (it = Config.Opers.begin(), it_end = Config.Opers.end(); it != it_end; ++it)
+ {
+ ci::string nick = it->first;
+
+ if (stristr(u->nick.c_str(), nick.c_str()) && !is_oper(u))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
+ return MOD_CONT;
+ }
+ }
+
+ if (Config.NSForceEmail && !email)
+ this->OnSyntaxError(u, "");
+ else if (time(NULL) < u->lastnickreg + Config.NSRegDelay)
+ notice_lang(Config.s_NickServ, u, NICK_REG_PLEASE_WAIT, (u->lastnickreg + Config.NSRegDelay) - time(NULL));
+ else if ((na = findnick(u->nick)))
+ {
+ /* i.e. there's already such a nick regged */
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ Alog() << Config.s_NickServ << ": " << u->GetIdent() << "@" << u->host << " tried to register FORBIDden nick " << u->nick;
+ notice_lang(Config.s_NickServ, u, NICK_CANNOT_BE_REGISTERED, u->nick.c_str());
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_ALREADY_REGISTERED, u->nick.c_str());
+ }
+ else if (!stricmp(u->nick.c_str(), pass) || (Config.StrictPasswords && strlen(pass) < 5))
+ notice_lang(Config.s_NickServ, u, MORE_OBSCURE_PASSWORD);
+ else if (strlen(pass) > Config.PassLen)
+ notice_lang(Config.s_NickServ, u, PASSWORD_TOO_LONG);
+ else if (email && !MailValidate(email))
+ notice_lang(Config.s_NickServ, u, MAIL_X_INVALID, email);
+ else
+ {
+ for (idx = 0; idx < 9; ++idx)
+ passcode[idx] = chars[1 + static_cast<int>((static_cast<float>(max - min)) * getrandom16() / 65536.0) + min];
+ passcode[idx] = '\0';
+ nr = new NickRequest(u->nick);
+ nr->passcode = passcode;
+ enc_encrypt(pass, nr->password);
+ if (email)
+ nr->email = sstrdup(email);
+ nr->requested = time(NULL);
+ FOREACH_MOD(I_OnMakeNickRequest, OnMakeNickRequest(nr));
+ if (Config.NSEmailReg)
+ {
+ if (SendRegmail(u, nr))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_ENTER_REG_CODE, email, Config.s_NickServ);
+ Alog() << Config.s_NickServ << ": sent registration verification code to " << nr->email;
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": Unable to send registration verification mail";
+ notice_lang(Config.s_NickServ, u, NICK_REG_UNABLE);
+ delete nr;
+ return MOD_CONT;
+ }
+ }
+ else
+ {
+ std::vector<ci::string> empty_params;
+ return this->DoConfirm(u, empty_params);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_REGISTER);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ if (Config.NSForceEmail)
+ syntax_error(Config.s_NickServ, u, "REGISTER", NICK_REGISTER_SYNTAX_EMAIL);
+ else
+ syntax_error(Config.s_NickServ, u, "REGISTER", NICK_REGISTER_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_REGISTER);
+ }
+};
+
+class CommandNSResend : public Command
+{
+ public:
+ CommandNSResend() : Command("RESEND", 0, 0)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickRequest *nr = NULL;
+ if (Config.NSEmailReg)
+ {
+ if ((nr = findrequestnick(u->nick)))
+ {
+ if (time(NULL) < nr->lastmail + Config.NSResendDelay)
+ {
+ notice_lang(Config.s_NickServ, u, MAIL_LATER);
+ return MOD_CONT;
+ }
+ if (!SendRegmail(u, nr))
+ {
+ nr->lastmail = time(NULL);
+ notice_lang(Config.s_NickServ, u, NICK_REG_RESENT, nr->email);
+ Alog() << Config.s_NickServ << ": re-sent registration verification code for " << nr->nick << " to " << nr->email;
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": Unable to re-send registration verification mail for " << nr->nick;
+ return MOD_CONT;
+ }
+ }
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_RESEND);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_RESEND);
+ }
+};
+
+class NSRegister : public Module
+{
+ public:
+ NSRegister(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSRegister());
+ this->AddCommand(NickServ, new CommandNSConfirm("CONFIRM", 1, 1));
+ this->AddCommand(NickServ, new CommandNSResend());
+ }
+};
+
+static bool SendRegmail(User *u, NickRequest *nr)
+{
+ char subject[BUFSIZE], message[BUFSIZE];
+
+ snprintf(subject, sizeof(subject), getstring(NICK_REG_MAIL_SUBJECT), nr->nick);
+ snprintf(message, sizeof(message), getstring(NICK_REG_MAIL), nr->nick, Config.NetworkName, Config.s_NickServ, nr->passcode.c_str(), Config.NetworkName);
+
+ return Mail(u, nr, Config.s_NickServ, subject, message);
+}
+
+MODULE_INIT(NSRegister)
diff --git a/modules/core/ns_release.cpp b/modules/core/ns_release.cpp
new file mode 100644
index 000000000..d2b6eaa03
--- /dev/null
+++ b/modules/core/ns_release.cpp
@@ -0,0 +1,106 @@
+/* NickServ 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 CommandNSRelease : public Command
+{
+ public:
+ CommandNSRelease() : Command("RELEASE", 1, 2)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ std::string pass = params.size() > 1 ? params[1].c_str() : "";
+ NickAlias *na;
+
+ if (!(na = findnick(nick)))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else if (na->nc->HasFlag(NI_SUSPENDED))
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, na->nick);
+ else if (!(na->HasFlag(NS_HELD)))
+ notice_lang(Config.s_NickServ, u, NICK_RELEASE_NOT_HELD, nick);
+ else if (!pass.empty())
+ {
+ int res = enc_check_password(pass, na->nc->pass);
+ if (res == 1)
+ {
+ na->Release();
+ notice_lang(Config.s_NickServ, u, NICK_RELEASED);
+ }
+ else
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ if (!res)
+ {
+ Alog() << Config.s_NickServ << ": RELEASE: invalid password for " << nick << " by " << u->GetMask();
+ if (bad_password(u))
+ return MOD_STOP;
+ }
+ }
+ }
+ else
+ {
+ if (u->Account() == na->nc || (!(na->nc->HasFlag(NI_SECURE)) && is_on_access(u, na->nc)))
+ {
+ na->Release();
+ notice_lang(Config.s_NickServ, u, NICK_RELEASED);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ char relstr[192];
+
+ /* Convert Config.NSReleaseTimeout seconds to string format */
+ duration(u->Account(), relstr, sizeof(relstr), Config.NSReleaseTimeout);
+
+ notice_help(Config.s_NickServ, u, NICK_HELP_RELEASE, relstr);
+ //do_help_limited(Config.s_NickServ, u, this);
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "RELEASE", NICK_RELEASE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_RELEASE);
+ }
+};
+
+class NSRelease : public Module
+{
+ public:
+ NSRelease(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSRelease());
+ }
+};
+
+MODULE_INIT(NSRelease)
diff --git a/modules/core/ns_resetpass.cpp b/modules/core/ns_resetpass.cpp
new file mode 100644
index 000000000..25c598d13
--- /dev/null
+++ b/modules/core/ns_resetpass.cpp
@@ -0,0 +1,169 @@
+/* NickServ 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"
+
+static bool SendResetEmail(User *u, NickAlias *na);
+
+class CommandNSResetPass : public Command
+{
+ public:
+ CommandNSResetPass() : Command("RESETPASS", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na;
+
+ if (Config.RestrictMail && !u->Account()->HasCommand("nickserv/resetpass"))
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ if (!(na = findnick(params[0])))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, params[0].c_str());
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else
+ {
+ if (SendResetEmail(u, na))
+ {
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used RESETPASS on " << na->nick << " (" << na->nc->display << ")";
+ notice_lang(Config.s_NickServ, u, NICK_RESETPASS_COMPLETE, na->nick);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_RESETPASS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "RESETPASS", NICK_RESETPASS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_RESETPASS);
+ }
+};
+
+class NSResetPass : public Module
+{
+ public:
+ NSResetPass(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!Config.UseMail)
+ throw ModuleException("Not using mail.");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSResetPass());
+
+ ModuleManager::Attach(I_OnPreCommand, this);
+ }
+
+ EventReturn OnPreCommand(User *u, const std::string &service, const ci::string &command, const std::vector<ci::string> &params)
+ {
+ if (service == Config.s_NickServ && command == "CONFIRM" && !params.empty())
+ {
+ NickAlias *na = findnick(u->nick);
+
+ time_t t;
+ std::string c;
+ if (na && na->nc->GetExtRegular("ns_resetpass_code", c) && na->nc->GetExtRegular("ns_resetpass_time", t))
+ {
+ if (t < time(NULL) - 3600)
+ {
+ na->nc->Shrink("ns_resetpass_code");
+ na->nc->Shrink("ns_resetpass_time");
+ notice_lang(Config.s_NickServ, u, NICK_CONFIRM_EXPIRED);
+ return EVENT_STOP;
+ }
+
+ std::string passcode = params[0].c_str();
+ if (passcode == c)
+ {
+ na->nc->Shrink("ns_resetpass_code");
+ na->nc->Shrink("ns_resetpass_time");
+
+ u->UpdateHost();
+ if (na->last_realname)
+ delete [] na->last_realname;
+ na->last_realname = sstrdup(u->realname);
+ na->last_seen = time(NULL);
+ u->Login(na->nc);
+ ircdproto->SendAccountLogin(u, u->Account());
+ ircdproto->SetAutoIdentificationToken(u);
+ FOREACH_MOD(I_OnNickIdentify, OnNickIdentify(u));
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used CONFIRM with RESETPASS to forcefully identify to " << na->nick;
+ notice_lang(Config.s_NickServ, u, NICK_CONFIRM_SUCCESS, Config.s_NickServ);
+
+ if (ircd->vhost)
+ do_on_id(u);
+ if (Config.NSModeOnID)
+ do_setmodes(u);
+ check_memos(u);
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": Invalid CONFIRM passcode for " << na->nick << " from " << u->GetMask();
+ notice_lang(Config.s_NickServ, u, NICK_CONFIRM_INVALID);
+ bad_password(u);
+ }
+
+ return EVENT_STOP;
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+};
+
+static bool SendResetEmail(User *u, NickAlias *na)
+{
+ char subject[BUFSIZE], message[BUFSIZE], passcode[20];
+
+ snprintf(subject, sizeof(subject), getstring(na, NICK_RESETPASS_SUBJECT), na->nick);
+
+ int min = 1, max = 62;
+ int chars[] = {
+ ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
+ 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
+ };
+
+ int idx;
+ for (idx = 0; idx < 20; ++idx)
+ passcode[idx] = chars[1 + static_cast<int>((static_cast<float>(max - min)) * getrandom16() / 65536.0) + min];
+ passcode[idx] = '\0';
+
+ snprintf(message, sizeof(message), getstring(na, NICK_RESETPASS_MESSAGE), na->nick, Config.s_NickServ, passcode, Config.NetworkName);
+
+ na->nc->Shrink("ns_resetpass_code");
+ na->nc->Shrink("ns_resetpass_time");
+
+ na->nc->Extend("ns_resetpass_code", new ExtensibleItemRegular<std::string>(passcode));
+ na->nc->Extend("ns_resetpass_time", new ExtensibleItemRegular<time_t>(time(NULL)));
+
+ return Mail(u, na->nc, Config.s_NickServ, subject, message);
+}
+
+MODULE_INIT(NSResetPass)
diff --git a/modules/core/ns_saset.cpp b/modules/core/ns_saset.cpp
new file mode 100644
index 000000000..2b8150f18
--- /dev/null
+++ b/modules/core/ns_saset.cpp
@@ -0,0 +1,245 @@
+/* NickServ 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 CommandNSSASet : public Command
+{
+ std::map<ci::string, Command *> subcommands;
+ public:
+ CommandNSSASet(const ci::string &cname) : Command(cname, 2, 4)
+ {
+ }
+
+ ~CommandNSSASet()
+ {
+ for (std::map<ci::string, Command *>::const_iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ delete it->second;
+ this->subcommands.clear();
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ ci::string cmd = params[1];
+
+ if (readonly)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SASET_DISABLED);
+ return MOD_CONT;
+ }
+
+ NickAlias *na = findnick(nick);
+ if (!na)
+ notice_lang(Config.s_NickServ, u, NICK_SASET_BAD_NICK, nick);
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else if (na->nc->HasFlag(NI_SUSPENDED))
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, na->nick);
+ else
+ {
+ Command *c = this->FindCommand(params[1]);
+
+ if (c)
+ {
+ ci::string cmdparams = na->nc->display;
+ for (std::vector<ci::string>::const_iterator it = params.begin() + 1; it != params.end(); ++it)
+ cmdparams += " " + *it;
+ mod_run_cmd(NickServ, u, c, params[1], cmdparams);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_SASET_UNKNOWN_OPTION, cmd.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_HEAD);
+ for (std::map<ci::string, Command *>::iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ it->second->OnServHelp(u);
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_TAIL);
+ return true;
+ }
+ else
+ {
+ Command *c = this->FindCommand(subcommand);
+
+ if (c)
+ return c->OnHelp(u, subcommand);
+ }
+
+ return false;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET", NICK_SASET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET);
+ }
+
+ bool AddSubcommand(Command *c)
+ {
+ return this->subcommands.insert(std::make_pair(c->name, c)).second;
+ }
+
+ bool DelSubcommand(const ci::string &command)
+ {
+ return this->subcommands.erase(command);
+ }
+
+ Command *FindCommand(const ci::string &subcommand)
+ {
+ std::map<ci::string, Command *>::const_iterator it = this->subcommands.find(subcommand);
+
+ if (it != this->subcommands.end())
+ return it->second;
+
+ return NULL;
+ }
+};
+
+class CommandNSSASetDisplay : public Command
+{
+ public:
+ CommandNSSASetDisplay(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/display")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ NickAlias *na = findnick(params[1]);
+ if (!na || na->nc != nc)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SASET_DISPLAY_INVALID, nc->display);
+ return MOD_CONT;
+ }
+
+ change_core_display(nc, params[1].c_str());
+ notice_lang(Config.s_NickServ, u, NICK_SASET_DISPLAY_CHANGED, nc->display);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_DISPLAY);
+ return true;
+ }
+
+ void OnSyntaxError(User *u)
+ {
+ // XXX
+ syntax_error(Config.s_NickServ, u, "SASET", NICK_SASET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_DISPLAY);
+ }
+};
+
+class CommandNSSASetPassword : public Command
+{
+ public:
+ CommandNSSASetPassword(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/password")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ size_t len = params[1].size();
+
+ if (Config.NSSecureAdmins && u->Account() != nc && nc->IsServicesOper())
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ else if (nc->display == params[1] || (Config.StrictPasswords && len < 5))
+ {
+ notice_lang(Config.s_NickServ, u, MORE_OBSCURE_PASSWORD);
+ return MOD_CONT;
+ }
+ else if (len > Config.PassLen)
+ {
+ notice_lang(Config.s_NickServ, u, PASSWORD_TOO_LONG);
+ return MOD_CONT;
+ }
+
+ std::string buf = params[1].c_str();
+ if (enc_encrypt(buf, nc->pass))
+ {
+ Alog() << Config.s_NickServ << ": Failed to encrypt password for " << nc->display << " (saset)";
+ notice_lang(Config.s_NickServ, u, NICK_SASET_PASSWORD_FAILED, nc->display);
+ return MOD_CONT;
+ }
+
+ std::string tmp_pass;
+ if (enc_decrypt(nc->pass, tmp_pass) == 1)
+ notice_lang(Config.s_NickServ, u, NICK_SASET_PASSWORD_CHANGED_TO, nc->display, tmp_pass.c_str());
+ else
+ notice_lang(Config.s_NickServ, u, NICK_SASET_PASSWORD_CHANGED, nc->display);
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used SASET PASSWORD on " << nc->display << " (e-mail: "<< (nc->email ? nc->email : "none") << ")";
+
+ if (Config.WallSetpass)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used SASET PASSWORD on \2%s\2", u->nick.c_str(), nc->display);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_PASSWORD);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET", NICK_SASET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_PASSWORD);
+ }
+};
+
+class NSSASet : public Module
+{
+public:
+ NSSASet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = new CommandNSSASet("SASET");
+ this->AddCommand(NickServ, c);
+ c->AddSubcommand(new CommandNSSASetDisplay("DISPLAY"));
+ c->AddSubcommand(new CommandNSSASetPassword("PASSWORD"));
+ }
+};
+
+MODULE_INIT(NSSASet)
diff --git a/modules/core/ns_saset_noexpire.cpp b/modules/core/ns_saset_noexpire.cpp
new file mode 100644
index 000000000..e68486350
--- /dev/null
+++ b/modules/core/ns_saset_noexpire.cpp
@@ -0,0 +1,84 @@
+/* NickServ 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 CommandNSSASetNoexpire : public Command
+{
+ public:
+ CommandNSSASetNoexpire(const ci::string &cname) : Command(cname, 1, 2, "nickserv/saset/noexpire")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na = findnick(params[0]);
+ assert(na);
+
+ ci::string param = params.size() > 1 ? params[1] : "";
+
+ if (param == "ON")
+ {
+ na->SetFlag(NS_NO_EXPIRE);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_NOEXPIRE_ON, na->nick);
+ }
+ else if (param == "OFF")
+ {
+ na->UnsetFlag(NS_NO_EXPIRE);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_NOEXPIRE_OFF, na->nick);
+ }
+ else
+ this->OnSyntaxError(u, "NOEXPIRE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_NOEXPIRE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET NOEXPIRE", NICK_SASET_NOEXPIRE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_NOEXPIRE);
+ }
+};
+
+class NSSASetNoexpire : public Module
+{
+ public:
+ NSSASetNoexpire(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetNoexpire("NOEXPIRE"));
+ }
+
+ ~NSSASetNoexpire()
+ {
+ Command *c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("NOEXPIRE");
+ }
+};
+
+MODULE_INIT(NSSASetNoexpire)
diff --git a/modules/core/ns_sendpass.cpp b/modules/core/ns_sendpass.cpp
new file mode 100644
index 000000000..d75f38e8f
--- /dev/null
+++ b/modules/core/ns_sendpass.cpp
@@ -0,0 +1,100 @@
+/* NickServ 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"
+
+static bool SendPassMail(User *u, NickAlias *na, const std::string &pass);
+
+class CommandNSSendPass : public Command
+{
+ public:
+ CommandNSSendPass() : Command("SENDPASS", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ NickAlias *na;
+
+ if (Config.RestrictMail && !u->Account()->HasCommand("nickserv/sendpass"))
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ else if (!(na = findnick(nick)))
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ else if (na->HasFlag(NS_FORBIDDEN))
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ else
+ {
+ std::string tmp_pass;
+ if (enc_decrypt(na->nc->pass,tmp_pass) == 1)
+ {
+ if (SendPassMail(u, na, tmp_pass))
+ {
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used SENDPASS on " << nick;
+ notice_lang(Config.s_NickServ, u, NICK_SENDPASS_OK, nick);
+ }
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_SENDPASS_UNAVAILABLE);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SENDPASS);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "SENDPASS", NICK_SENDPASS_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SENDPASS);
+ }
+};
+
+class NSSendPass : public Module
+{
+ public:
+ NSSendPass(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!Config.UseMail)
+ throw ModuleException("Not using mail, whut.");
+
+ std::string tmp_pass = "plain:tmp";
+ if (enc_decrypt(tmp_pass, tmp_pass) == -1)
+ throw ModuleException("Incompatible with the encryption module being used");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSSendPass());
+ }
+};
+
+static bool SendPassMail(User *u, NickAlias *na, const std::string &pass)
+{
+ char subject[BUFSIZE], message[BUFSIZE];
+
+ snprintf(subject, sizeof(subject), getstring(na, NICK_SENDPASS_SUBJECT), na->nick);
+ snprintf(message, sizeof(message), getstring(na, NICK_SENDPASS), na->nick, pass.c_str(), Config.NetworkName);
+
+ return Mail(u, na->nc, Config.s_NickServ, subject, message);
+}
+
+MODULE_INIT(NSSendPass)
diff --git a/modules/core/ns_set.cpp b/modules/core/ns_set.cpp
new file mode 100644
index 000000000..f858c6a9c
--- /dev/null
+++ b/modules/core/ns_set.cpp
@@ -0,0 +1,229 @@
+/* NickServ 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 CommandNSSet : public Command
+{
+ std::map<ci::string, Command *> subcommands;
+ public:
+ CommandNSSet(const ci::string &cname) : Command(cname, 1, 3)
+ {
+ }
+
+ ~CommandNSSet()
+ {
+ for (std::map<ci::string, Command *>::const_iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ delete it->second;
+ this->subcommands.clear();
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SET_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (u->Account()->HasFlag(NI_SUSPENDED))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_SUSPENDED, u->Account()->display);
+ return MOD_CONT;
+ }
+
+ Command *c = this->FindCommand(params[0]);
+
+ if (c)
+ {
+ ci::string cmdparams;
+ for (std::vector<ci::string>::const_iterator it = params.begin() + 1, it_end = params.end(); it != it_end; ++it)
+ cmdparams += " " + *it;
+ if (!cmdparams.empty())
+ cmdparams.erase(cmdparams.begin());
+ mod_run_cmd(NickServ, u, c, params[0], cmdparams);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_SET_UNKNOWN_OPTION, params[0].c_str());
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_HEAD);
+ for (std::map<ci::string, Command *>::iterator it = this->subcommands.begin(), it_end = this->subcommands.end(); it != it_end; ++it)
+ it->second->OnServHelp(u);
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_TAIL);
+ return true;
+ }
+ else
+ {
+ Command *c = this->FindCommand(subcommand);
+
+ if (c)
+ return c->OnHelp(u, subcommand);
+ }
+
+ return false;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "SET", NICK_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET);
+ }
+
+ bool AddSubcommand(Command *c)
+ {
+ return this->subcommands.insert(std::make_pair(c->name, c)).second;
+ }
+
+ bool DelSubcommand(const ci::string &command)
+ {
+ return this->subcommands.erase(command);
+ }
+
+ Command *FindCommand(const ci::string &subcommand)
+ {
+ std::map<ci::string, Command *>::const_iterator it = this->subcommands.find(subcommand);
+
+ if (it != this->subcommands.end())
+ return it->second;
+
+ return NULL;
+ }
+};
+
+class CommandNSSetDisplay : public Command
+{
+ public:
+ CommandNSSetDisplay(const ci::string &cname) : Command(cname, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na = findnick(params[0]);
+
+ if (!na || na->nc != u->Account())
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SET_DISPLAY_INVALID);
+ return MOD_CONT;
+ }
+
+ change_core_display(u->Account(), params[0].c_str());
+ notice_lang(Config.s_NickServ, u, NICK_SET_DISPLAY_CHANGED, u->Account()->display);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_DISPLAY);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_NickServ, u, "SET", NICK_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_DISPLAY);
+ }
+};
+
+class CommandNSSetPassword : public Command
+{
+ public:
+ CommandNSSetPassword(const ci::string &cname) : Command(cname, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string param = params[0];
+
+ int len = param.size();
+
+ if (u->Account()->display == param || (Config.StrictPasswords && len < 5))
+ {
+ notice_lang(Config.s_NickServ, u, MORE_OBSCURE_PASSWORD);
+ return MOD_CONT;
+ }
+ else if (len > Config.PassLen)
+ {
+ notice_lang(Config.s_NickServ, u, PASSWORD_TOO_LONG);
+ return MOD_CONT;
+ }
+
+ std::string buf = param.c_str(); /* conversion from ci::string to std::string */
+ if (enc_encrypt(buf, u->Account()->pass) < 0)
+ {
+ Alog() << Config.s_NickServ << ": Failed to encrypt password for " << u->Account()->display << " (set)";
+ notice_lang(Config.s_NickServ, u, NICK_SET_PASSWORD_FAILED);
+ return MOD_CONT;
+ }
+
+ std::string tmp_pass;
+ if (enc_decrypt(u->Account()->pass, tmp_pass) == 1)
+ notice_lang(Config.s_NickServ, u, NICK_SET_PASSWORD_CHANGED_TO, tmp_pass.c_str());
+ else
+ notice_lang(Config.s_NickServ, u, NICK_SET_PASSWORD_CHANGED);
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " (e-mail: " << (u->Account()->email ? u->Account()->email : "none") << ") changed its password.";
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_PASSWORD);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ // XXX
+ syntax_error(Config.s_NickServ, u, "SET", NICK_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_PASSWORD);
+ }
+};
+
+class NSSet : public Module
+{
+ public:
+ NSSet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *set = new CommandNSSet("SET");
+ this->AddCommand(NickServ, set);
+ set->AddSubcommand(new CommandNSSetDisplay("DISPLAY"));
+ set->AddSubcommand(new CommandNSSetPassword("PASSWORD"));
+ }
+};
+
+MODULE_INIT(NSSet)
diff --git a/modules/core/ns_set_autoop.cpp b/modules/core/ns_set_autoop.cpp
new file mode 100644
index 000000000..93e637f97
--- /dev/null
+++ b/modules/core/ns_set_autoop.cpp
@@ -0,0 +1,128 @@
+/* NickServ 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 CommandNSSetAutoOp : public Command
+{
+ public:
+ CommandNSSetAutoOp(const ci::string &cname) : Command(cname, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (params[0] == "ON")
+ {
+ u->Account()->SetFlag(NI_AUTOOP);
+ notice_lang(Config.s_NickServ, u, NICK_SET_AUTOOP_ON);
+ }
+ else if (params[0] == "OFF")
+ {
+ u->Account()->UnsetFlag(NI_AUTOOP);
+ notice_lang(Config.s_NickServ, u, NICK_SET_AUTOOP_OFF);
+ }
+ else
+ this->OnSyntaxError(u, "AUTOOP");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_AUTOOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET AUTOOP", NICK_SET_AUTOOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_AUTOOP);
+ }
+};
+
+class CommandNSSASetAutoOp : public Command
+{
+ public:
+ CommandNSSASetAutoOp(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/autoop")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ ci::string param = params[1];
+
+ if (param == "ON")
+ {
+ nc->SetFlag(NI_AUTOOP);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_AUTOOP_ON, nc->display);
+ }
+ else if (param == "OFF")
+ {
+ nc->UnsetFlag(NI_AUTOOP);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_AUTOOP_OFF, nc->display);
+ }
+ else
+ this->OnSyntaxError(u, "AUTOOP");
+
+ return MOD_CONT;
+ }
+
+ bool Help(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_AUTOOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET AUTOOP", NICK_SASET_AUTOOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_AUTOOP);
+ }
+};
+
+class NSSetAutoOp : public Module
+{
+ public:
+ NSSetAutoOp(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *set = FindCommand(NickServ, "SET");
+
+ if (set)
+ set->AddSubcommand(new CommandNSSetAutoOp("AUTOOP"));
+ }
+
+ ~NSSetAutoOp()
+ {
+ Command *set = FindCommand(NickServ, "SET");
+
+ if (set)
+ set->DelSubcommand("AUTOOP");
+ }
+};
+
+MODULE_INIT(NSSetAutoOp)
diff --git a/modules/core/ns_set_email.cpp b/modules/core/ns_set_email.cpp
new file mode 100644
index 000000000..6d3e60ecb
--- /dev/null
+++ b/modules/core/ns_set_email.cpp
@@ -0,0 +1,157 @@
+/* NickServ 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 CommandNSSetEmail : public Command
+{
+ public:
+ CommandNSSetEmail(const ci::string &cname) : Command(cname, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (params.empty() && Config.NSForceEmail)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SET_EMAIL_UNSET_IMPOSSIBLE);
+ return MOD_CONT;
+ }
+ else if (!params.empty() && !MailValidate(params[0].c_str()))
+ {
+ notice_lang(Config.s_NickServ, u, MAIL_X_INVALID, params[0].c_str());
+ return MOD_CONT;
+ }
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " (e-mail: " << (u->Account()->email ? u->Account()->email : "none") << ") changed its e-mail to " << (!params.empty() ? params[0] : "none");
+
+ if (u->Account()->email)
+ delete [] u->Account()->email;
+
+ if (!params.empty())
+ {
+ u->Account()->email = sstrdup(params[0].c_str());
+ notice_lang(Config.s_NickServ, u, NICK_SET_EMAIL_CHANGED, params[0].c_str());
+ }
+ else
+ {
+ u->Account()->email = NULL;
+ notice_lang(Config.s_NickServ, u, NICK_SET_EMAIL_UNSET);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_EMAIL);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_EMAIL);
+ }
+};
+
+class CommandNSSASetEmail : public Command
+{
+ public:
+ CommandNSSASetEmail(const ci::string &cname) : Command(cname, 1, 2, "nickserv/saset/email")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ const char *param = params.size() > 1 ? params[1].c_str() : NULL;
+
+ if (!param && Config.NSForceEmail)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SASET_EMAIL_UNSET_IMPOSSIBLE);
+ return MOD_CONT;
+ }
+ else if (Config.NSSecureAdmins && u->Account() != nc && nc->IsServicesOper())
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+ else if (param && !MailValidate(param))
+ {
+ notice_lang(Config.s_NickServ, u, MAIL_X_INVALID, param);
+ return MOD_CONT;
+ }
+
+ Alog() << Config.s_NickServ << ": " << u->GetMask() << " used SASET EMAIL on " << nc->display << " (e-mail: " << (nc->email ? nc->email : "none") << ")";
+
+ if (nc->email)
+ delete [] nc->email;
+
+ if (param)
+ {
+ nc->email = sstrdup(param);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_EMAIL_CHANGED, nc->display, param);
+ }
+ else
+ {
+ nc->email = NULL;
+ notice_lang(Config.s_NickServ, u, NICK_SASET_EMAIL_UNSET, nc->display);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_EMAIL);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_EMAIL);
+ }
+};
+
+class NSSetEmail : public Module
+{
+ public:
+ NSSetEmail(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandNSSetEmail("EMAIL"));
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetEmail("EMAIL"));
+ }
+
+ ~NSSetEmail()
+ {
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->DelSubcommand("EMAIL");
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("EMAIL");
+ }
+};
+
+MODULE_INIT(NSSetEmail)
diff --git a/modules/core/ns_set_greet.cpp b/modules/core/ns_set_greet.cpp
new file mode 100644
index 000000000..c33b90991
--- /dev/null
+++ b/modules/core/ns_set_greet.cpp
@@ -0,0 +1,127 @@
+/* NickServ 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 CommandNSSetGreet : public Command
+{
+ public:
+ CommandNSSetGreet(const ci::string &cname) : Command(cname, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (u->Account()->greet)
+ delete [] u->Account()->greet;
+
+ if (!params.empty())
+ {
+ u->Account()->greet = sstrdup(params[0].c_str());
+ notice_lang(Config.s_NickServ, u, NICK_SET_GREET_CHANGED, u->Account()->greet);
+ }
+ else
+ {
+ u->Account()->greet = NULL;
+ notice_lang(Config.s_NickServ, u, NICK_SET_GREET_UNSET);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_GREET);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_GREET);
+ }
+};
+
+class CommandNSSASetGreet : public Command
+{
+ public:
+ CommandNSSASetGreet(const ci::string &cname) : Command(cname, 1, 2, "nickserv/saset/greet")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ const char *param = params.size() > 1 ? params[1].c_str() : NULL;
+
+ if (nc->greet)
+ delete [] nc->greet;
+
+ if (param)
+ {
+ nc->greet = sstrdup(param);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_GREET_CHANGED, nc->display, nc->greet);
+ }
+ else
+ {
+ nc->greet = NULL;
+ notice_lang(Config.s_NickServ, u, NICK_SASET_GREET_UNSET, nc->display);
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_GREET);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_GREET);
+ }
+};
+
+class NSSetGreet : public Module
+{
+ public:
+ NSSetGreet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetVersion("$Id$");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandNSSetGreet("GREET"));
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetGreet("GREET"));
+ }
+
+ ~NSSetGreet()
+ {
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->DelSubcommand("GREET");
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("GREET");
+ }
+};
+
+MODULE_INIT(NSSetGreet)
diff --git a/modules/core/ns_set_hide.cpp b/modules/core/ns_set_hide.cpp
new file mode 100644
index 000000000..160b0f0fd
--- /dev/null
+++ b/modules/core/ns_set_hide.cpp
@@ -0,0 +1,197 @@
+/* NickServ 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 CommandNSSetHide : public Command
+{
+ public:
+ CommandNSSetHide(const ci::string &cname) : Command(cname, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ int onmsg, offmsg;
+ NickCoreFlag flag;
+
+ if (params[0] == "EMAIL")
+ {
+ flag = NI_HIDE_EMAIL;
+ onmsg = NICK_SET_HIDE_EMAIL_ON;
+ offmsg = NICK_SET_HIDE_EMAIL_OFF;
+ }
+ else if (params[0] == "USERMASK")
+ {
+ flag = NI_HIDE_MASK;
+ onmsg = NICK_SET_HIDE_MASK_ON;
+ offmsg = NICK_SET_HIDE_MASK_OFF;
+ }
+ else if (params[0] == "STATUS")
+ {
+ flag = NI_HIDE_STATUS;
+ onmsg = NICK_SET_HIDE_STATUS_ON;
+ offmsg = NICK_SET_HIDE_STATUS_OFF;
+ }
+ else if (params[0] == "QUIT")
+ {
+ flag = NI_HIDE_QUIT;
+ onmsg = NICK_SET_HIDE_QUIT_ON;
+ offmsg = NICK_SET_HIDE_QUIT_OFF;
+ }
+ else
+ {
+ this->OnSyntaxError(u, "HIDE");
+ return MOD_CONT;
+ }
+
+ if (params[1] == "ON")
+ {
+ u->Account()->SetFlag(flag);
+ notice_lang(Config.s_NickServ, u, onmsg, Config.s_NickServ);
+ }
+ else if (params[1] == "OFF")
+ {
+ u->Account()->UnsetFlag(flag);
+ notice_lang(Config.s_NickServ, u, offmsg, Config.s_NickServ);
+ }
+ else
+ this->OnSyntaxError(u, "HIDE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_HIDE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET HIDE", NICK_SET_HIDE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_HIDE);
+ }
+};
+
+class CommandNSSASetHide : public Command
+{
+ public:
+ CommandNSSASetHide(const ci::string &cname) : Command(cname, 3, 3, "nickserv/saset/command")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ ci::string param = params[1];
+
+ int onmsg, offmsg;
+ NickCoreFlag flag;
+
+ if (param == "EMAIL")
+ {
+ flag = NI_HIDE_EMAIL;
+ onmsg = NICK_SASET_HIDE_EMAIL_ON;
+ offmsg = NICK_SASET_HIDE_EMAIL_OFF;
+ }
+ else if (param == "USERMASK")
+ {
+ flag = NI_HIDE_MASK;
+ onmsg = NICK_SASET_HIDE_MASK_ON;
+ offmsg = NICK_SASET_HIDE_MASK_OFF;
+ }
+ else if (param == "STATUS")
+ {
+ flag = NI_HIDE_STATUS;
+ onmsg = NICK_SASET_HIDE_STATUS_ON;
+ offmsg = NICK_SASET_HIDE_STATUS_OFF;
+ }
+ else if (param == "QUIT")
+ {
+ flag = NI_HIDE_QUIT;
+ onmsg = NICK_SASET_HIDE_QUIT_ON;
+ offmsg = NICK_SASET_HIDE_QUIT_OFF;
+ }
+ else
+ {
+ this->OnSyntaxError(u, "HIDE");
+ return MOD_CONT;
+ }
+
+ param = params[2];
+ if (param.empty())
+ this->OnSyntaxError(u, "HIDE");
+ else if (param == "ON")
+ {
+ nc->SetFlag(flag);
+ notice_lang(Config.s_NickServ, u, onmsg, nc->display, Config.s_NickServ);
+ }
+ else if (param == "OFF")
+ {
+ nc->UnsetFlag(flag);
+ notice_lang(Config.s_NickServ, u, offmsg, nc->display, Config.s_NickServ);
+ }
+ else
+ this->OnSyntaxError(u, "HIDE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_HIDE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET HIDE", NICK_SASET_HIDE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_HIDE);
+ }
+};
+
+class NSSetHide : public Module
+{
+ public:
+ NSSetHide(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *set = FindCommand(NickServ, "SET");
+
+ if (set)
+ set->AddSubcommand(new CommandNSSetHide("HIDE"));
+ }
+
+ ~NSSetHide()
+ {
+ Command *set = FindCommand(NickServ, "SET");
+
+ if (set)
+ set->DelSubcommand("HIDE");
+ }
+};
+
+MODULE_INIT(NSSetHide)
diff --git a/modules/core/ns_set_kill.cpp b/modules/core/ns_set_kill.cpp
new file mode 100644
index 000000000..f996fdb5b
--- /dev/null
+++ b/modules/core/ns_set_kill.cpp
@@ -0,0 +1,180 @@
+/* NickServ 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 CommandNSSetKill : public Command
+{
+ public:
+ CommandNSSetKill(const ci::string &cname) : Command(cname, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (params[0] == "ON")
+ {
+ u->Account()->SetFlag(NI_KILLPROTECT);
+ u->Account()->UnsetFlag(NI_KILL_QUICK);
+ u->Account()->UnsetFlag(NI_KILL_IMMED);
+ notice_lang(Config.s_NickServ, u, NICK_SET_KILL_ON);
+ }
+ else if (params[0] == "QUICK")
+ {
+ u->Account()->SetFlag(NI_KILLPROTECT);
+ u->Account()->SetFlag(NI_KILL_QUICK);
+ u->Account()->UnsetFlag(NI_KILL_IMMED);
+ notice_lang(Config.s_NickServ, u, NICK_SET_KILL_QUICK);
+ }
+ else if (params[0] == "IMMED")
+ {
+ if (Config.NSAllowKillImmed)
+ {
+ u->Account()->SetFlag(NI_KILLPROTECT);
+ u->Account()->SetFlag(NI_KILL_IMMED);
+ u->Account()->UnsetFlag(NI_KILL_QUICK);
+ notice_lang(Config.s_NickServ, u, NICK_SET_KILL_IMMED);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_SET_KILL_IMMED_DISABLED);
+ }
+ else if (params[0] == "OFF")
+ {
+ u->Account()->UnsetFlag(NI_KILLPROTECT);
+ u->Account()->UnsetFlag(NI_KILL_QUICK);
+ u->Account()->UnsetFlag(NI_KILL_IMMED);
+ notice_lang(Config.s_NickServ, u, NICK_SET_KILL_OFF);
+ }
+ else
+ this->OnSyntaxError(u, "KILL");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_KILL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET KILL", Config.NSAllowKillImmed ? NICK_SET_KILL_IMMED_SYNTAX : NICK_SET_KILL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_KILL);
+ }
+};
+
+class CommandNSSASetKill : public Command
+{
+ public:
+ CommandNSSASetKill(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/kill")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ ci::string param = params[1];
+
+ if (param == "ON")
+ {
+ nc->SetFlag(NI_KILLPROTECT);
+ nc->UnsetFlag(NI_KILL_QUICK);
+ nc->UnsetFlag(NI_KILL_IMMED);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_KILL_ON, nc->display);
+ }
+ else if (param == "QUICK")
+ {
+ nc->SetFlag(NI_KILLPROTECT);
+ nc->SetFlag(NI_KILL_QUICK);
+ nc->UnsetFlag(NI_KILL_IMMED);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_KILL_QUICK, nc->display);
+ }
+ else if (param == "IMMED")
+ {
+ if (Config.NSAllowKillImmed)
+ {
+ nc->SetFlag(NI_KILLPROTECT);
+ nc->SetFlag(NI_KILL_IMMED);
+ nc->UnsetFlag(NI_KILL_QUICK);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_KILL_IMMED, nc->display);
+ }
+ else
+ notice_lang(Config.s_NickServ, u, NICK_SASET_KILL_IMMED_DISABLED);
+ }
+ else if (param == "OFF")
+ {
+ nc->UnsetFlag(NI_KILLPROTECT);
+ nc->UnsetFlag(NI_KILL_QUICK);
+ nc->UnsetFlag(NI_KILL_IMMED);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_KILL_OFF, nc->display);
+ }
+ else
+ this->OnSyntaxError(u, "KILL");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_KILL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET KILL", Config.NSAllowKillImmed ? NICK_SASET_KILL_IMMED_SYNTAX : NICK_SASET_KILL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_KILL);
+ }
+};
+
+class NSSetKill : public Module
+{
+ public:
+ NSSetKill(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandNSSetKill("KILL"));
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetKill("KILL"));
+ }
+
+ ~NSSetKill()
+ {
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->DelSubcommand("KILL");
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("KILL");
+ }
+};
+
+MODULE_INIT(NSSetKill)
diff --git a/modules/core/ns_set_language.cpp b/modules/core/ns_set_language.cpp
new file mode 100644
index 000000000..00d4f5864
--- /dev/null
+++ b/modules/core/ns_set_language.cpp
@@ -0,0 +1,140 @@
+/* NickServ 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 CommandNSSetLanguage : public Command
+{
+ public:
+ CommandNSSetLanguage(const ci::string &cname) : Command(cname, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *param = params[0].c_str();
+
+ if (param[strspn(param, "0123456789")]) /* i.e. not a number */
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ int langnum = atoi(param) - 1;
+ if (langnum < 0 || langnum >= NUM_LANGS || langlist[langnum] < 0)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SET_LANGUAGE_UNKNOWN, langnum + 1, Config.s_NickServ);
+ return MOD_CONT;
+ }
+
+ u->Account()->language = langlist[langnum];
+ notice_lang(Config.s_NickServ, u, NICK_SET_LANGUAGE_CHANGED);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_LANGUAGE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET LANGUAGE", NICK_SET_LANGUAGE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_LANGUAGE);
+ }
+};
+
+class CommandNSSASetLanguage : public Command
+{
+ public:
+ CommandNSSASetLanguage(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/language")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ const char *param = params[1].c_str();
+
+ if (param[strspn(param, "0123456789")]) /* i.e. not a number */
+ {
+ this->OnSyntaxError(u, "LANGUAGE");
+ return MOD_CONT;
+ }
+ int langnum = atoi(param) - 1;
+ if (langnum < 0 || langnum >= NUM_LANGS || langlist[langnum] < 0)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SASET_LANGUAGE_UNKNOWN, langnum + 1, Config.s_NickServ);
+ return MOD_CONT;
+ }
+ nc->language = langlist[langnum];
+ notice_lang(Config.s_NickServ, u, NICK_SASET_LANGUAGE_CHANGED);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_LANGUAGE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET LANGUAGE", NICK_SASET_LANGUAGE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_LANGUAGE);
+ }
+};
+
+class NSSetLanguage : public Module
+{
+ public:
+ NSSetLanguage(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandNSSetLanguage("LANGUAGE"));
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetLanguage("LANGUAGE"));
+ }
+
+ ~NSSetLanguage()
+ {
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->DelSubcommand("LANGUAGE");
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("LANGUAGE");
+ }
+};
+
+MODULE_INIT(NSSetLanguage)
diff --git a/modules/core/ns_set_message.cpp b/modules/core/ns_set_message.cpp
new file mode 100644
index 000000000..600872c01
--- /dev/null
+++ b/modules/core/ns_set_message.cpp
@@ -0,0 +1,146 @@
+/* NickServ 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 CommandNSSetMessage : public Command
+{
+ public:
+ CommandNSSetMessage(const ci::string &cname) : Command(cname, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (!Config.UsePrivmsg)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SET_OPTION_DISABLED, "MSG");
+ return MOD_CONT;
+ }
+
+ if (params[0] == "ON")
+ {
+ u->Account()->SetFlag(NI_MSG);
+ notice_lang(Config.s_NickServ, u, NICK_SET_MSG_ON);
+ }
+ else if (params[0] == "OFF")
+ {
+ u->Account()->UnsetFlag(NI_MSG);
+ notice_lang(Config.s_NickServ, u, NICK_SET_MSG_OFF);
+ }
+ else
+ this->OnSyntaxError(u, "MSG");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_MSG);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET MSG", NICK_SET_MSG_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_MSG);
+ }
+};
+
+class CommandNSSASetMessage : public Command
+{
+ public:
+ CommandNSSASetMessage(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/message")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ ci::string param = params[1];
+
+ if (!Config.UsePrivmsg)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_SASET_OPTION_DISABLED, "MSG");
+ return MOD_CONT;
+ }
+
+ if (param == "ON")
+ {
+ nc->SetFlag(NI_MSG);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_MSG_ON, nc->display);
+ }
+ else if (param == "OFF")
+ {
+ nc->UnsetFlag(NI_MSG);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_MSG_OFF, nc->display);
+ }
+ else
+ this->OnSyntaxError(u, "MSG");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_MSG);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET MSG", NICK_SASET_MSG_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_MSG);
+ }
+};
+
+class NSSetMessage : public Module
+{
+ public:
+ NSSetMessage(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandNSSetMessage("MSG"));
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetMessage("MSG"));
+ }
+
+ ~NSSetMessage()
+ {
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->DelSubcommand("MSG");
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("MSG");
+ }
+};
+
+MODULE_INIT(NSSetMessage)
diff --git a/modules/core/ns_set_private.cpp b/modules/core/ns_set_private.cpp
new file mode 100644
index 000000000..da18a8b44
--- /dev/null
+++ b/modules/core/ns_set_private.cpp
@@ -0,0 +1,134 @@
+/* NickServ 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 CommandNSSetPrivate : public Command
+{
+ public:
+ CommandNSSetPrivate(const ci::string &cname) : Command(cname, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (params[0] == "ON")
+ {
+ u->Account()->SetFlag(NI_PRIVATE);
+ notice_lang(Config.s_NickServ, u, NICK_SET_PRIVATE_ON);
+ }
+ else if (params[0] == "OFF")
+ {
+ u->Account()->UnsetFlag(NI_PRIVATE);
+ notice_lang(Config.s_NickServ, u, NICK_SET_PRIVATE_OFF);
+ }
+ else
+ this->OnSyntaxError(u, "PRIVATE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_PRIVATE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET PRIVATE", NICK_SET_PRIVATE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_CMD_SET_PRIVATE);
+ }
+};
+
+class CommandNSSASetPrivate : public Command
+{
+ public:
+ CommandNSSASetPrivate(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/private")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ ci::string param = params[1];
+
+ if (param == "ON")
+ {
+ nc->SetFlag(NI_PRIVATE);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_PRIVATE_ON, nc->display);
+ }
+ else if (param == "OFF")
+ {
+ nc->UnsetFlag(NI_PRIVATE);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_PRIVATE_OFF, nc->display);
+ }
+ else
+ this->OnSyntaxError(u, "PRIVATE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_PRIVATE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET PRIVATE", NICK_SASET_PRIVATE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_CMD_SASET_PRIVATE);
+ }
+};
+
+class NSSetPrivate : public Module
+{
+ public:
+ NSSetPrivate(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandNSSetPrivate("PRIVATE"));
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetPrivate("PRIVATE"));
+ }
+
+ ~NSSetPrivate()
+ {
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->DelSubcommand("PRIVATE");
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("PRIVATE");
+ }
+};
+
+MODULE_INIT(NSSetPrivate)
diff --git a/modules/core/ns_set_secure.cpp b/modules/core/ns_set_secure.cpp
new file mode 100644
index 000000000..ce627d9d4
--- /dev/null
+++ b/modules/core/ns_set_secure.cpp
@@ -0,0 +1,134 @@
+/* NickServ 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 CommandNSSetSecure : public Command
+{
+ public:
+ CommandNSSetSecure(const ci::string &cname) : Command(cname, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (params[0] == "ON")
+ {
+ u->Account()->SetFlag(NI_SECURE);
+ notice_lang(Config.s_NickServ, u, NICK_SET_SECURE_ON);
+ }
+ else if (params[0] == "OFF")
+ {
+ u->Account()->UnsetFlag(NI_SECURE);
+ notice_lang(Config.s_NickServ, u, NICK_SET_SECURE_OFF);
+ }
+ else
+ this->OnSyntaxError(u, "SECURE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SET_SECURE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SET SECURE", NICK_SET_SECURE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SET_SECURE);
+ }
+};
+
+class CommandNSSASetSecure : public Command
+{
+ public:
+ CommandNSSASetSecure(const ci::string &cname) : Command(cname, 2, 2, "nickserv/saset/secure")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickCore *nc = findcore(params[0]);
+ assert(nc);
+
+ ci::string param = params[1];
+
+ if (param == "ON")
+ {
+ nc->SetFlag(NI_SECURE);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_SECURE_ON, nc->display);
+ }
+ else if (param == "OFF")
+ {
+ nc->UnsetFlag(NI_SECURE);
+ notice_lang(Config.s_NickServ, u, NICK_SASET_SECURE_OFF, nc->display);
+ }
+ else
+ this->OnSyntaxError(u, "SECURE");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_SASET_SECURE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &)
+ {
+ syntax_error(Config.s_NickServ, u, "SASET SECURE", NICK_SASET_SECURE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SASET_SECURE);
+ }
+};
+
+class NSSetSecure : public Module
+{
+ public:
+ NSSetSecure(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->AddSubcommand(new CommandNSSetSecure("SECURE"));
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->AddSubcommand(new CommandNSSASetSecure("SECURE"));
+ }
+
+ ~NSSetSecure()
+ {
+ Command *c = FindCommand(NickServ, "SET");
+ if (c)
+ c->DelSubcommand("SECURE");
+
+ c = FindCommand(NickServ, "SASET");
+ if (c)
+ c->DelSubcommand("SECURE");
+ }
+};
+
+MODULE_INIT(NSSetSecure)
diff --git a/modules/core/ns_status.cpp b/modules/core/ns_status.cpp
new file mode 100644
index 000000000..a213db474
--- /dev/null
+++ b/modules/core/ns_status.cpp
@@ -0,0 +1,77 @@
+/* NickServ 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 CommandNSStatus : public Command
+{
+ public:
+ CommandNSStatus() : Command("STATUS", 0, 16)
+ {
+ this->SetFlag(CFLAG_ALLOW_UNREGISTERED);
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ User *u2;
+ NickAlias *na = NULL;
+ std::string nick = params.size() ? params[0].c_str() : u->nick;
+ spacesepstream sep(nick);
+ std::string nickbuf;
+
+ while (sep.GetToken(nickbuf))
+ {
+ na = findnick(nick);
+
+ if (!(u2 = finduser(nickbuf))) /* Nick is not online */
+ notice_lang(Config.s_NickServ, u, NICK_STATUS_REPLY, nickbuf.c_str(), 0, "");
+ else if (u2->IsIdentified() && na && na->nc == u2->Account()) /* Nick is identified */
+ notice_lang(Config.s_NickServ, u, NICK_STATUS_REPLY, nickbuf.c_str(), 3, u2->Account()->display);
+ else if (u2->IsRecognized()) /* Nick is recognised, but NOT identified */
+ notice_lang(Config.s_NickServ, u, NICK_STATUS_REPLY, nickbuf.c_str(), 2, (u2->Account() ? u2->Account()->display : ""));
+ else if (!na) /* Nick is online, but NOT a registered */
+ notice_lang(Config.s_NickServ, u, NICK_STATUS_REPLY, nickbuf.c_str(), 0, "");
+ else
+ /* Nick is not identified for the nick, but they could be logged into an account,
+ * so we tell the user about it
+ */
+ notice_lang(Config.s_NickServ, u, NICK_STATUS_REPLY, nickbuf.c_str(), 1, (u2->Account() ? u2->Account()->display : ""));
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_STATUS);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_STATUS);
+ }
+};
+
+class NSStatus : public Module
+{
+ public:
+ NSStatus(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSStatus());
+ }
+};
+
+MODULE_INIT(NSStatus)
diff --git a/modules/core/ns_suspend.cpp b/modules/core/ns_suspend.cpp
new file mode 100644
index 000000000..6564ca9f8
--- /dev/null
+++ b/modules/core/ns_suspend.cpp
@@ -0,0 +1,199 @@
+/* NickServ 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 CommandNSSuspend : public Command
+{
+ public:
+ CommandNSSuspend() : Command("SUSPEND", 2, 2, "nickserv/suspend")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na;
+ User *u2;
+ const char *nick = params[0].c_str();
+ const char *reason = params[1].c_str();
+
+ if (readonly)
+ {
+ notice_lang(Config.s_NickServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+
+ if (!(na = findnick(nick)))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ return MOD_CONT;
+ }
+
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ return MOD_CONT;
+ }
+
+ if (Config.NSSecureAdmins && na->nc->IsServicesOper())
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (na)
+ {
+ na->nc->SetFlag(NI_SUSPENDED);
+ na->nc->SetFlag(NI_SECURE);
+ na->nc->UnsetFlag(NI_KILLPROTECT);
+ na->nc->UnsetFlag(NI_KILL_QUICK);
+ na->nc->UnsetFlag(NI_KILL_IMMED);
+
+ for (std::list<NickAlias *>::iterator it = na->nc->aliases.begin(), it_end = na->nc->aliases.end(); it != it_end; ++it)
+ {
+ NickAlias *na2 = *it;
+
+ if (na2->nc == na->nc)
+ {
+ if (na2->last_quit)
+ delete [] na2->last_quit;
+ na2->last_quit = sstrdup(reason);
+
+ if ((u2 = finduser(na2->nick)))
+ {
+ u2->Logout();
+ u2->Collide(na2);
+ }
+ }
+ }
+
+ if (Config.WallForbid)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used SUSPEND on \2%s\2", u->nick.c_str(), nick);
+
+ Alog() << Config.s_NickServ << ": " << u->nick << " set SUSPEND for nick " << nick;
+ notice_lang(Config.s_NickServ, u, NICK_SUSPEND_SUCCEEDED, nick);
+
+ FOREACH_MOD(I_OnNickSuspended, OnNickSuspend(na));
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": Valid SUSPEND for " << nick << " by " << u->nick << " failed";
+ notice_lang(Config.s_NickServ, u, NICK_SUSPEND_FAILED, nick);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_SUSPEND);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "SUSPEND", NICK_SUSPEND_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_SUSPEND);
+ }
+};
+
+class CommandNSUnSuspend : public Command
+{
+ public:
+ CommandNSUnSuspend() : Command("UNSUSPEND", 1, 1, "nickserv/suspend")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na;
+ const char *nick = params[0].c_str();
+
+ if (readonly)
+ {
+ notice_lang(Config.s_NickServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+
+ if (!(na = findnick(nick)))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_NOT_REGISTERED, nick);
+ return MOD_CONT;
+ }
+
+ if (na->HasFlag(NS_FORBIDDEN))
+ {
+ notice_lang(Config.s_NickServ, u, NICK_X_FORBIDDEN, na->nick);
+ return MOD_CONT;
+ }
+
+ if (Config.NSSecureAdmins && na->nc->IsServicesOper())
+ {
+ notice_lang(Config.s_NickServ, u, ACCESS_DENIED);
+ return MOD_CONT;
+ }
+
+ if (na)
+ {
+ na->nc->UnsetFlag(NI_SUSPENDED);
+
+ if (Config.WallForbid)
+ ircdproto->SendGlobops(NickServ, "\2%s\2 used UNSUSPEND on \2%s\2", u->nick.c_str(), nick);
+
+ Alog() << Config.s_NickServ << ": " << u->nick << " set UNSUSPEND for nick " << nick;
+ notice_lang(Config.s_NickServ, u, NICK_UNSUSPEND_SUCCEEDED, nick);
+
+ FOREACH_MOD(I_OnNickUnsuspended, OnNickUnsuspended(na));
+ }
+ else
+ {
+ Alog() << Config.s_NickServ << ": Valid UNSUSPEND for " << nick << " by " << u->nick << " failed";
+ notice_lang(Config.s_NickServ, u, NICK_UNSUSPEND_FAILED, nick);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_NickServ, u, NICK_SERVADMIN_HELP_UNSUSPEND);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_NickServ, u, "UNSUSPEND", NICK_UNSUSPEND_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_UNSUSPEND);
+ }
+};
+
+class NSSuspend : public Module
+{
+ public:
+ NSSuspend(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSSuspend());
+ this->AddCommand(NickServ, new CommandNSUnSuspend());
+ }
+};
+
+MODULE_INIT(NSSuspend)
diff --git a/modules/core/ns_update.cpp b/modules/core/ns_update.cpp
new file mode 100644
index 000000000..7a2e287b8
--- /dev/null
+++ b/modules/core/ns_update.cpp
@@ -0,0 +1,67 @@
+/* NickServ 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 CommandNSUpdate : public Command
+{
+ public:
+ CommandNSUpdate() : Command("UPDATE", 0, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ NickAlias *na = findnick(u->nick);
+
+ if (!na)
+ return MOD_CONT;
+
+ do_setmodes(u);
+ check_memos(u);
+
+ if (na->last_realname)
+ delete [] na->last_realname;
+ na->last_realname = sstrdup(u->realname);
+ na->last_seen = time(NULL);
+ if (ircd->vhost)
+ do_on_id(u);
+ notice_lang(Config.s_NickServ, u, NICK_UPDATE_SUCCESS, Config.s_NickServ);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &)
+ {
+ notice_help(Config.s_NickServ, u, NICK_HELP_UPDATE);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_NickServ, u, NICK_HELP_CMD_UPDATE);
+ }
+};
+
+class NSUpdate : public Module
+{
+ public:
+ NSUpdate(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(NickServ, new CommandNSUpdate());
+ }
+};
+
+MODULE_INIT(NSUpdate)
diff --git a/modules/core/os_akill.cpp b/modules/core/os_akill.cpp
new file mode 100644
index 000000000..f572ec123
--- /dev/null
+++ b/modules/core/os_akill.cpp
@@ -0,0 +1,410 @@
+/* OperServ 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 AkillDelCallback : public NumberList
+{
+ User *u;
+ unsigned Deleted;
+ public:
+ AkillDelCallback(User *_u, const std::string &numlist) : NumberList(numlist, true), u(_u), Deleted(0)
+ {
+ }
+
+ ~AkillDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_NO_MATCH);
+ else if (Deleted == 1)
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_DELETED_ONE);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_DELETED_SEVERAL, Deleted);
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SGLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ ++Deleted;
+ DoDel(u, x);
+ }
+
+ static void DoDel(User *u, XLine *x)
+ {
+ SGLine->DelXLine(x);
+ }
+};
+
+class AkillListCallback : public NumberList
+{
+ protected:
+ User *u;
+ bool SentHeader;
+ public:
+ AkillListCallback(User *_u, const std::string &numlist) : NumberList(numlist, false), u(_u), SentHeader(false)
+ {
+ }
+
+ ~AkillListCallback()
+ {
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_NO_MATCH);
+ else
+ notice_lang(Config.s_OperServ, u, END_OF_ANY_LIST, "Akill");
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SGLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_LIST_HEADER);
+ }
+
+ DoList(u, x, Number);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str());
+ }
+};
+
+class AkillViewCallback : public AkillListCallback
+{
+ public:
+ AkillViewCallback(User *_u, const std::string &numlist) : AkillListCallback(_u, numlist)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SGLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_VIEW_HEADER);
+ }
+
+ DoList(u, x, Number);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ char timebuf[32], expirebuf[256];
+ struct tm tm;
+
+ tm = *localtime(&x->Created);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, &tm);
+ expire_left(u->Account(), expirebuf, sizeof(expirebuf), x->Expires);
+
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), timebuf, expirebuf, x->Reason.c_str());
+ }
+};
+
+class CommandOSAKill : public Command
+{
+ private:
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params)
+ {
+ unsigned last_param = 2;
+ const char *expiry, *mask;
+ char reason[BUFSIZE];
+ time_t expires;
+
+ mask = params.size() > 1 ? params[1].c_str() : NULL;
+ if (mask && *mask == '+')
+ {
+ expiry = mask;
+ mask = params.size() > 2 ? params[2].c_str() : NULL;
+ last_param = 3;
+ }
+ else
+ expiry = NULL;
+
+ expires = expiry ? dotime(expiry) : Config.AutokillExpiry;
+ /* If the expiry given does not contain a final letter, it's in days,
+ * said the doc. Ah well.
+ */
+ if (expiry && isdigit(expiry[strlen(expiry) - 1]))
+ expires *= 86400;
+ /* Do not allow less than a minute expiry time */
+ if (expires != 0 && expires < 60)
+ {
+ notice_lang(Config.s_OperServ, u, BAD_EXPIRY_TIME);
+ return MOD_CONT;
+ }
+ else if (expires > 0)
+ expires += time(NULL);
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+ snprintf(reason, sizeof(reason), "%s%s%s", params[last_param].c_str(), last_param == 2 && params.size() > 3 ? " " : "", last_param == 2 && params.size() > 3 ? params[3].c_str() : "");
+ if (mask && *reason)
+ {
+ XLine *x = SGLine->Add(OperServ, u, mask, expires, reason);
+
+ if (!x)
+ return MOD_CONT;
+
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_ADDED, mask);
+
+ if (Config.WallOSAkill)
+ {
+ char buf[128];
+
+ if (!expires)
+ strcpy(buf, "does not expire");
+ else
+ {
+ int wall_expiry = expires - time(NULL);
+ const char *s = NULL;
+
+ if (wall_expiry >= 86400)
+ {
+ wall_expiry /= 86400;
+ s = "day";
+ }
+ else if (wall_expiry >= 3600)
+ {
+ wall_expiry /= 3600;
+ s = "hour";
+ }
+ else if (wall_expiry >= 60)
+ {
+ wall_expiry /= 60;
+ s = "minute";
+ }
+
+ snprintf(buf, sizeof(buf), "expires in %d %s%s", wall_expiry, s, wall_expiry == 1 ? "" : "s");
+ }
+
+ ircdproto->SendGlobops(OperServ, "%s added an AKILL for %s (%s) (%s)", u->nick.c_str(), mask, reason, buf);
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+ }
+ else
+ this->OnSyntaxError(u, "ADD");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params)
+ {
+ const ci::string mask = params.size() > 1 ? params[0] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (SGLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ if (isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ AkillDelCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ XLine *x = SGLine->HasEntry(mask);
+
+ if (!x)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_NOT_FOUND, mask.c_str());
+ return MOD_CONT;
+ }
+
+ FOREACH_MOD(I_OnDelAkill, OnDelAkill(u, x));
+
+ AkillDelCallback::DoDel(u, x);
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_DELETED, mask.c_str());
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, const std::vector<ci::string> &params)
+ {
+ if (SGLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ AkillListCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SGLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SGLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_LIST_HEADER);
+ }
+
+ AkillListCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_NO_MATCH);
+ else
+ notice_lang(Config.s_OperServ, u, END_OF_ANY_LIST, "Akill");
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoView(User *u, const std::vector<ci::string> &params)
+ {
+ if (SGLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ AkillViewCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SGLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SGLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_VIEW_HEADER);
+ }
+
+ AkillViewCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_NO_MATCH);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoClear(User *u)
+ {
+ FOREACH_MOD(I_OnDelAkill, OnDelAkill(u, NULL));
+ SGLine->Clear();
+ notice_lang(Config.s_OperServ, u, OPER_AKILL_CLEAR);
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSAKill() : Command("AKILL", 1, 4, "operserv/akill")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (cmd == "ADD")
+ return this->DoAdd(u, params);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params);
+ else if (cmd == "LIST")
+ return this->DoList(u, params);
+ else if (cmd == "VIEW")
+ return this->DoView(u, params);
+ else if (cmd == "CLEAR")
+ return this->DoClear(u);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_AKILL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "AKILL", OPER_AKILL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_AKILL);
+ }
+};
+
+class OSAKill : public Module
+{
+ public:
+ OSAKill(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSAKill());
+ }
+};
+
+MODULE_INIT(OSAKill)
diff --git a/modules/core/os_chankill.cpp b/modules/core/os_chankill.cpp
new file mode 100644
index 000000000..f751d6dee
--- /dev/null
+++ b/modules/core/os_chankill.cpp
@@ -0,0 +1,117 @@
+/* OperServ 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 CommandOSChanKill : public Command
+{
+ public:
+ CommandOSChanKill() : Command("CHANKILL", 2, 3, "operserv/chankill")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *expiry, *channel;
+ char reason[BUFSIZE];
+ time_t expires;
+ unsigned last_param = 1;
+ Channel *c;
+
+ channel = params[0].c_str();
+ if (channel && *channel == '+')
+ {
+ expiry = channel;
+ channel = params[1].c_str();
+ last_param = 2;
+ }
+ else
+ expiry = NULL;
+
+ expires = expiry ? dotime(expiry) : Config.ChankillExpiry;
+ if (expiry && isdigit(expiry[strlen(expiry) - 1]))
+ expires *= 86400;
+ if (expires != 0 && expires < 60)
+ {
+ notice_lang(Config.s_OperServ, u, BAD_EXPIRY_TIME);
+ return MOD_CONT;
+ }
+ else if (expires > 0)
+ expires += time(NULL);
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+ snprintf(reason, sizeof(reason), "%s%s", params[last_param].c_str(), (params.size() > last_param + 1 ? params[last_param + 1].c_str() : ""));
+ if (*reason)
+ {
+
+ std::string realreason;
+ if (Config.AddAkiller)
+ realreason = "[" + u->nick + "] " + std::string(reason);
+ else
+ realreason = reason;
+
+ if ((c = findchan(channel)))
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; )
+ {
+ UserContainer *uc = *it++;
+
+ if (is_oper(uc->user))
+ continue;
+
+ SGLine->Add(OperServ, u, ci::string("*@") + uc->user->host, expires, realreason);
+ SGLine->Check(uc->user);
+ }
+ if (Config.WallOSAkill)
+ ircdproto->SendGlobops(OperServ, "%s used CHANKILL on %s (%s)", u->nick.c_str(), channel, realreason.c_str());
+ }
+ else
+ notice_lang(Config.s_OperServ, u, CHAN_X_NOT_IN_USE, channel);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_CHANKILL);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "CHANKILL", OPER_CHANKILL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_CHANKILL);
+ }
+};
+
+class OSChanKill : public Module
+{
+ public:
+ OSChanKill(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSChanKill());
+ }
+};
+
+MODULE_INIT(OSChanKill)
diff --git a/modules/core/os_chanlist.cpp b/modules/core/os_chanlist.cpp
new file mode 100644
index 000000000..d1a154d2f
--- /dev/null
+++ b/modules/core/os_chanlist.cpp
@@ -0,0 +1,99 @@
+/* OperServ 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 CommandOSChanList : public Command
+{
+ public:
+ CommandOSChanList() : Command("CHANLIST", 0, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *pattern = params.size() > 0 ? params[0].c_str() : NULL;
+ ci::string opt = params.size() > 1 ? params[1] : "";
+ std::list<ChannelModeName> Modes;
+ User *u2;
+
+ if (!opt.empty() && opt == "SECRET")
+ {
+ Modes.push_back(CMODE_SECRET);
+ Modes.push_back(CMODE_PRIVATE);
+ }
+
+ if (pattern && (u2 = finduser(pattern)))
+ {
+ notice_lang(Config.s_OperServ, u, OPER_CHANLIST_HEADER_USER, u2->nick.c_str());
+
+ for (UChannelList::iterator uit = u2->chans.begin(), uit_end = u2->chans.end(); uit != uit_end; ++uit)
+ {
+ ChannelContainer *cc = *uit;
+
+ if (!Modes.empty())
+ for (std::list<ChannelModeName>::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it)
+ if (!cc->chan->HasMode(*it))
+ continue;
+
+ notice_lang(Config.s_OperServ, u, OPER_CHANLIST_RECORD, cc->chan->name.c_str(), cc->chan->users.size(), chan_get_modes(cc->chan, 1, 1), cc->chan->topic ? cc->chan->topic : "");
+ }
+ }
+ else
+ {
+ notice_lang(Config.s_OperServ, u, OPER_CHANLIST_HEADER);
+
+ for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit)
+ {
+ Channel *c = cit->second;
+
+ if (pattern && !Anope::Match(c->name, pattern, false))
+ continue;
+ if (!Modes.empty())
+ for (std::list<ChannelModeName>::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it)
+ if (!c->HasMode(*it))
+ continue;
+
+ notice_lang(Config.s_OperServ, u, OPER_CHANLIST_RECORD, c->name.c_str(), c->users.size(), chan_get_modes(c, 1, 1), c->topic ? c->topic : "");
+ }
+ }
+
+ notice_lang(Config.s_OperServ, u, OPER_CHANLIST_END);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_CHANLIST);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_CHANLIST);
+ }
+};
+
+class OSChanList : public Module
+{
+ public:
+ OSChanList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSChanList());
+ }
+};
+
+MODULE_INIT(OSChanList)
diff --git a/modules/core/os_clearmodes.cpp b/modules/core/os_clearmodes.cpp
new file mode 100644
index 000000000..3c73846c4
--- /dev/null
+++ b/modules/core/os_clearmodes.cpp
@@ -0,0 +1,189 @@
+/* OperServ 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 CommandOSClearModes : public Command
+{
+ public:
+ CommandOSClearModes() : Command("CLEARMODES", 1, 2, "operserv/clearmodes")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str();
+ Channel *c;
+ int all = 0;
+ ChannelMode *cm;
+ std::string buf;
+
+ if (!(c = findchan(chan)))
+ {
+ notice_lang(Config.s_OperServ, u, CHAN_X_NOT_IN_USE, chan);
+ return MOD_CONT;
+ }
+ else if (c->bouncy_modes)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_BOUNCY_MODES_U_LINE);
+ return MOD_CONT;
+ }
+ else
+ {
+ ci::string s = params.size() > 1 ? params[1] : "";
+ if (!s.empty())
+ {
+ if (s == "ALL")
+ all = 1;
+ else
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+ }
+
+ if (Config.WallOSClearmodes)
+ ircdproto->SendGlobops(OperServ, "%s used CLEARMODES%s on %s", u->nick.c_str(), all ? " ALL" : "", chan);
+ if (all)
+ {
+ /* Clear mode +o */
+ if (ircd->svsmode_ucmode)
+ ircdproto->SendSVSModeChan(c, "-o", NULL);
+ else
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ UserContainer *uc = *it;
+
+ if (uc->Status->HasFlag(CMODE_OP))
+ c->RemoveMode(NULL, CMODE_OP, uc->user->nick);
+ }
+ }
+
+ /* Clear mode +v */
+ if (ircd->svsmode_ucmode)
+ ircdproto->SendSVSModeChan(c, "-v", NULL);
+ else
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ UserContainer *uc = *it;
+
+ if (uc->Status->HasFlag(CMODE_VOICE))
+ c->RemoveMode(NULL, CMODE_VOICE, uc->user->nick);
+ }
+ }
+
+ /* Clear mode +h */
+ if (ModeManager::FindChannelModeByName(CMODE_HALFOP))
+ {
+ if (ircd->svsmode_ucmode)
+ ircdproto->SendSVSModeChan(c, "-h", NULL);
+ else
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ UserContainer *uc = *it;
+
+ if (uc->Status->HasFlag(CMODE_HALFOP))
+ c->RemoveMode(NULL, CMODE_HALFOP, uc->user->nick);
+ }
+ }
+ }
+
+ /* Clear mode Owners */
+ if ((cm = ModeManager::FindChannelModeByName(CMODE_OWNER)))
+ {
+ buf = '-';
+ buf += cm->ModeChar;
+
+ if (ircd->svsmode_ucmode)
+ ircdproto->SendSVSModeChan(c, buf.c_str(), NULL);
+ else
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ UserContainer *uc = *it;
+
+ if (uc->Status->HasFlag(CMODE_OWNER))
+ c->RemoveMode(NULL, CMODE_OWNER, uc->user->nick);
+ }
+ }
+ }
+
+ /* Clear mode protected or admins */
+ if ((cm = ModeManager::FindChannelModeByName(CMODE_PROTECT)))
+ {
+ buf = '-';
+ buf += cm->ModeChar;
+
+ if (ircd->svsmode_ucmode)
+ ircdproto->SendSVSModeChan(c, buf.c_str(), NULL);
+ else
+ {
+ for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
+ {
+ UserContainer *uc = *it;
+
+ if (uc->Status->HasFlag(CMODE_PROTECT))
+ c->RemoveMode(NULL, CMODE_PROTECT, uc->user->nick);
+ }
+ }
+ }
+ }
+
+
+ c->ClearModes();
+ c->ClearBans();
+ c->ClearExcepts();
+ c->ClearInvites();
+ }
+
+ if (all)
+ notice_lang(Config.s_OperServ, u, OPER_CLEARMODES_ALL_DONE, chan);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_CLEARMODES_DONE, chan);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_CLEARMODES);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "CLEARMODES", OPER_CLEARMODES_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_CLEARMODES);
+ }
+};
+
+class OSClearModes : public Module
+{
+ public:
+ OSClearModes(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSClearModes());
+ }
+};
+
+MODULE_INIT(OSClearModes)
diff --git a/modules/core/os_defcon.cpp b/modules/core/os_defcon.cpp
new file mode 100644
index 000000000..78e366071
--- /dev/null
+++ b/modules/core/os_defcon.cpp
@@ -0,0 +1,499 @@
+/* OperServ 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"
+
+void defcon_sendlvls(User *u);
+void runDefCon();
+void defconParseModeString(const char *str);
+void resetDefCon(int level);
+static char *defconReverseModes(const char *modes);
+class DefConTimeout;
+static DefConTimeout *timeout;
+
+class DefConTimeout : public Timer
+{
+ int level;
+
+ public:
+ DefConTimeout(int newlevel) : Timer(Config.DefConTimeOut), level(newlevel) { }
+
+ void Tick(time_t)
+ {
+ if (Config.DefConLevel != level)
+ {
+ Config.DefConLevel = level;
+ FOREACH_MOD(I_OnDefconLevel, OnDefconLevel(level));
+ Alog() << "Defcon level timeout, returning to lvl " << level;
+ ircdproto->SendGlobops(OperServ, getstring(OPER_DEFCON_WALL), Config.s_OperServ, level);
+
+ if (Config.GlobalOnDefcon)
+ {
+ if (Config.DefConOffMessage)
+ oper_global(NULL, "%s", Config.DefConOffMessage);
+ else
+ oper_global(NULL, getstring(DEFCON_GLOBAL), Config.DefConLevel);
+
+ if (Config.GlobalOnDefconMore && !Config.DefConOffMessage)
+ oper_global(NULL, "%s", Config.DefconMessage);
+ }
+
+ runDefCon();
+ }
+ }
+};
+
+class CommandOSDEFCON : public Command
+{
+ public:
+ CommandOSDEFCON() : Command("DEFCON", 1, 1, "operserv/defcon")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *lvl = params[0].c_str();
+ int newLevel = 0;
+ const char *langglobal = getstring(DEFCON_GLOBAL);
+
+ if (!lvl)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_DEFCON_CHANGED, Config.DefConLevel);
+ defcon_sendlvls(u);
+ return MOD_CONT;
+ }
+ newLevel = atoi(lvl);
+ if (newLevel < 1 || newLevel > 5)
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+ Config.DefConLevel = newLevel;
+
+ FOREACH_MOD(I_OnDefconLevel, OnDefconLevel(newLevel));
+
+ if (timeout)
+ {
+ delete timeout;
+ timeout = NULL;
+ }
+
+ if (Config.DefConTimeOut)
+ timeout = new DefConTimeout(5);
+
+ notice_lang(Config.s_OperServ, u, OPER_DEFCON_CHANGED, Config.DefConLevel);
+ defcon_sendlvls(u);
+ Alog() << "Defcon level changed to " << newLevel << " by Oper " << u->nick;
+ ircdproto->SendGlobops(OperServ, getstring(OPER_DEFCON_WALL), u->nick.c_str(), newLevel);
+ /* Global notice the user what is happening. Also any Message that
+ the Admin would like to add. Set in config file. */
+ if (Config.GlobalOnDefcon)
+ {
+ if (Config.DefConLevel == 5 && Config.DefConOffMessage)
+ oper_global(NULL, "%s", Config.DefConOffMessage);
+ else
+ oper_global(NULL, langglobal, Config.DefConLevel);
+ }
+ if (Config.GlobalOnDefconMore)
+ {
+ if (!Config.DefConOffMessage || Config.DefConLevel != 5)
+ oper_global(NULL, "%s", Config.DefconMessage);
+ }
+ /* Run any defcon functions, e.g. FORCE CHAN MODE */
+ runDefCon();
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_DEFCON);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "DEFCON", OPER_DEFCON_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_DEFCON);
+ }
+};
+
+class OSDEFCON : public Module
+{
+ public:
+ OSDEFCON(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!Config.DefConLevel)
+ throw ModuleException("Invalid configuration settings");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ Implementation i[] = { I_OnPreUserConnect, I_OnChannelModeSet, I_OnChannelModeUnset, I_OnPreCommandRun, I_OnPreCommand, I_OnUserConnect, I_OnChannelModeAdd, I_OnChannelCreate };
+ ModuleManager::Attach(i, this, 8);
+
+ this->AddCommand(OperServ, new CommandOSDEFCON());
+
+ defconParseModeString(Config.DefConChanModes);
+ }
+
+ EventReturn OnPreUserConnect(User *u)
+ {
+ if (u->server->IsSynced() && CheckDefCon(DEFCON_AKILL_NEW_CLIENTS) && !u->server->IsULined())
+ {
+ if (CheckDefCon(DEFCON_AKILL_NEW_CLIENTS))
+ {
+ Alog() << "DEFCON: adding akill for *@" << u->host;
+ XLine *x = SGLine->Add(NULL, NULL, ci::string("*@") + u->host, time(NULL) + Config.DefConAKILL, Config.DefConAkillReason ? Config.DefConAkillReason : "DEFCON AKILL");
+ if (x)
+ x->By = Config.s_OperServ;
+ }
+
+ if (CheckDefCon(DEFCON_NO_NEW_CLIENTS) || CheckDefCon(DEFCON_AKILL_NEW_CLIENTS))
+ kill_user(Config.s_OperServ, u->nick, Config.DefConAkillReason);
+
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const std::string &param)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(Name);
+
+ if (CheckDefCon(DEFCON_FORCE_CHAN_MODES) && cm && DefConModesOff.HasFlag(Name))
+ {
+ c->RemoveMode(OperServ, Name, param);
+
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnChannelModeUnset(Channel *c, ChannelModeName Name, const std::string &)
+ {
+ ChannelMode *cm = ModeManager::FindChannelModeByName(Name);
+
+ if (CheckDefCon(DEFCON_FORCE_CHAN_MODES) && cm && DefConModesOn.HasFlag(Name))
+ {
+ std::string param;
+
+ if (GetDefConParam(Name, param))
+ c->SetMode(OperServ, Name, param);
+ else
+ c->SetMode(OperServ, Name);
+
+ return EVENT_STOP;
+
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnPreCommandRun(User *u, BotInfo *bi, const ci::string &command, const ci::string &message, Command *c)
+ {
+ if (!c)
+ {
+ if (CheckDefCon(DEFCON_SILENT_OPER_ONLY) && !is_oper(u))
+ return EVENT_STOP;
+ }
+ if ((CheckDefCon(DEFCON_OPER_ONLY) || CheckDefCon(DEFCON_SILENT_OPER_ONLY)) && !is_oper(u))
+ {
+ if (!CheckDefCon(DEFCON_SILENT_OPER_ONLY))
+ notice_lang(bi->nick.c_str(), u, OPER_DEFCON_DENIED);
+
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ EventReturn OnPreCommand(User *u, const std::string &service, const ci::string &command, const std::vector<ci::string> &params)
+ {
+ if (service == Config.s_NickServ)
+ {
+ if (command == "SET")
+ {
+ if (!params.empty() && params[0] == "MLOCK" && CheckDefCon(DEFCON_NO_MLOCK_CHANGE))
+ {
+ notice_lang(Config.s_ChanServ, u, OPER_DEFCON_DENIED);
+ return EVENT_STOP;
+ }
+ }
+ else if (command == "REGISTER" || command == "GROUP")
+ {
+ if (CheckDefCon(DEFCON_NO_NEW_NICKS))
+ {
+ notice_lang(Config.s_NickServ, u, OPER_DEFCON_DENIED);
+ return EVENT_STOP;
+ }
+ }
+ }
+ else if (service == Config.s_ChanServ)
+ {
+ if (command == "REGISTER")
+ {
+ if (CheckDefCon(DEFCON_NO_NEW_CHANNELS))
+ {
+ notice_lang(Config.s_ChanServ, u, OPER_DEFCON_DENIED);
+ return EVENT_STOP;
+ }
+ }
+ }
+ else if (service == Config.s_MemoServ)
+ {
+ if (command == "SEND" || command == "SENDALL")
+ {
+ if (CheckDefCon(DEFCON_NO_NEW_MEMOS))
+ {
+ notice_lang(Config.s_MemoServ, u, OPER_DEFCON_DENIED);
+ return EVENT_STOP;
+ }
+ }
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnUserConnect(User *u)
+ {
+ Session *session = findsession(u->host);
+ Exception *exception = find_hostip_exception(u->host, u->hostip);
+
+ if (CheckDefCon(DEFCON_REDUCE_SESSION) && !exception)
+ {
+ if (session && session->count > Config.DefConSessionLimit)
+ {
+ if (Config.SessionLimitExceeded)
+ ircdproto->SendMessage(OperServ, u->nick.c_str(), Config.SessionLimitExceeded, u->host);
+ if (Config.SessionLimitDetailsLoc)
+ ircdproto->SendMessage(OperServ, u->nick.c_str(), "%s", Config.SessionLimitDetailsLoc);
+
+ kill_user(Config.s_OperServ, u->nick, "Session limit exceeded");
+ ++session->hits;
+ if (Config.MaxSessionKill && session->hits >= Config.MaxSessionKill)
+ {
+ SGLine->Add(NULL, NULL, ci::string("*@") + u->host, time(NULL) + Config.SessionAutoKillExpiry, "Session limit exceeded");
+ ircdproto->SendGlobops(OperServ, "Added a temporary AKILL for \2*@%s\2 due to excessive connections", u->host);
+ }
+ }
+ }
+ }
+
+ void OnChannelModeAdd(ChannelMode *cm)
+ {
+ if (Config.DefConChanModes)
+ {
+ std::string modes = Config.DefConChanModes;
+
+ if (modes.find(cm->ModeChar) != std::string::npos)
+ /* New mode has been added to Anope, check to see if defcon
+ * requires it
+ */
+ defconParseModeString(Config.DefConChanModes);
+ }
+ }
+
+ void OnChannelCreate(Channel *c)
+ {
+ if (CheckDefCon(DEFCON_FORCE_CHAN_MODES))
+ c->SetModes(OperServ, false, Config.DefConChanModes);
+ }
+};
+
+/**
+ * Send a message to the oper about which precautions are "active" for this level
+ **/
+void defcon_sendlvls(User *u)
+{
+ if (CheckDefCon(DEFCON_NO_NEW_CHANNELS))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_CHANNELS);
+ if (CheckDefCon(DEFCON_NO_NEW_NICKS))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_NICKS);
+ if (CheckDefCon(DEFCON_NO_MLOCK_CHANGE))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_NO_MLOCK_CHANGE);
+ if (CheckDefCon(DEFCON_FORCE_CHAN_MODES) && Config.DefConChanModes)
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_FORCE_CHAN_MODES, Config.DefConChanModes);
+ if (CheckDefCon(DEFCON_REDUCE_SESSION))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_REDUCE_SESSION, Config.DefConSessionLimit);
+ if (CheckDefCon(DEFCON_NO_NEW_CLIENTS))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_CLIENTS);
+ if (CheckDefCon(DEFCON_OPER_ONLY))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_OPER_ONLY);
+ if (CheckDefCon(DEFCON_SILENT_OPER_ONLY))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_SILENT_OPER_ONLY);
+ if (CheckDefCon(DEFCON_AKILL_NEW_CLIENTS))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_AKILL_NEW_CLIENTS);
+ if (CheckDefCon(DEFCON_NO_NEW_MEMOS))
+ notice_lang(Config.s_OperServ, u, OPER_HELP_DEFCON_NO_NEW_MEMOS);
+}
+
+void runDefCon()
+{
+ char *newmodes;
+
+ if (CheckDefCon(DEFCON_FORCE_CHAN_MODES))
+ {
+ if (Config.DefConChanModes && !DefConModesSet)
+ {
+ if (Config.DefConChanModes[0] == '+' || Config.DefConChanModes[0] == '-')
+ {
+ Alog() << "DEFCON: setting " << Config.DefConChanModes << " on all channels";
+ DefConModesSet = true;
+ MassChannelModes(OperServ, Config.DefConChanModes);
+ }
+ }
+ }
+ else
+ {
+ if (Config.DefConChanModes && DefConModesSet)
+ {
+ if (Config.DefConChanModes[0] == '+' || Config.DefConChanModes[0] == '-')
+ {
+ DefConModesSet = false;
+ if ((newmodes = defconReverseModes(Config.DefConChanModes)))
+ {
+ Alog() << "DEFCON: setting " << newmodes << " on all channels";
+ MassChannelModes(OperServ, newmodes);
+ delete [] newmodes;
+ }
+ }
+ }
+ }
+}
+
+/* Parse the defcon mlock mode string and set the correct global vars.
+ *
+ * @param str mode string to parse
+ * @return 1 if accepted, 0 if failed
+ */
+void defconParseModeString(const char *str)
+{
+ int add = -1; /* 1 if adding, 0 if deleting, -1 if neither */
+ unsigned char mode;
+ ChannelMode *cm;
+ ChannelModeParam *cmp;
+ std::string modes, param;
+
+ if (!str)
+ return;
+
+ spacesepstream ss(str);
+
+ DefConModesOn.ClearFlags();
+ DefConModesOff.ClearFlags();
+ ss.GetToken(modes);
+
+ /* Loop while there are modes to set */
+ for (unsigned i = 0, end = modes.size(); i < end; ++i)
+ {
+ mode = modes[i];
+
+ switch (mode)
+ {
+ case '+':
+ add = 1;
+ continue;
+ case '-':
+ add = 0;
+ continue;
+ default:
+ if (add < 0)
+ continue;
+ }
+
+ if ((cm = ModeManager::FindChannelModeByChar(mode)))
+ {
+ if (cm->Type == MODE_STATUS || cm->Type == MODE_LIST || !cm->CanSet(NULL))
+ {
+ Alog() << "DefConChanModes mode character '" << mode << "' cannot be locked";
+ continue;
+ }
+ else if (add)
+ {
+ DefConModesOn.SetFlag(cm->Name);
+ DefConModesOff.UnsetFlag(cm->Name);
+
+ if (cm->Type == MODE_PARAM)
+ {
+ cmp = dynamic_cast<ChannelModeParam *>(cm);
+
+ if (!ss.GetToken(param))
+ {
+ Alog() << "DefConChanModes mode character '" << mode << "' has no parameter while one is expected";
+ continue;
+ }
+
+ if (!cmp->IsValid(param))
+ continue;
+
+ SetDefConParam(cmp->Name, param);
+ }
+ }
+ else
+ {
+ if (DefConModesOn.HasFlag(cm->Name))
+ {
+ DefConModesOn.UnsetFlag(cm->Name);
+
+ if (cm->Type == MODE_PARAM)
+ UnsetDefConParam(cm->Name);
+ }
+ }
+ }
+ }
+
+ /* We can't mlock +L if +l is not mlocked as well. */
+ if ((cm = ModeManager::FindChannelModeByName(CMODE_REDIRECT)) && DefConModesOn.HasFlag(cm->Name) && !DefConModesOn.HasFlag(CMODE_LIMIT))
+ {
+ DefConModesOn.UnsetFlag(CMODE_REDIRECT);
+
+ Alog() << "DefConChanModes must lock mode +l as well to lock mode +L";
+ }
+
+ /* Some ircd we can't set NOKNOCK without INVITE */
+ /* So check if we need there is a NOKNOCK MODE and that we need INVITEONLY */
+ if (ircd->knock_needs_i && (cm = ModeManager::FindChannelModeByName(CMODE_NOKNOCK)) && DefConModesOn.HasFlag(cm->Name) && !DefConModesOn.HasFlag(CMODE_INVITE))
+ {
+ DefConModesOn.UnsetFlag(CMODE_NOKNOCK);
+ Alog() << "DefConChanModes must lock mode +i as well to lock mode +K";
+ }
+}
+
+static char *defconReverseModes(const char *modes)
+{
+ char *newmodes = NULL;
+ unsigned i = 0;
+ if (!modes)
+ return NULL;
+ if (!(newmodes = new char[strlen(modes) + 1]))
+ return NULL;
+ for (i = 0; i < strlen(modes); ++i)
+ {
+ if (modes[i] == '+')
+ newmodes[i] = '-';
+ else if (modes[i] == '-')
+ newmodes[i] = '+';
+ else
+ newmodes[i] = modes[i];
+ }
+ newmodes[i] = '\0';
+ return newmodes;
+}
+
+MODULE_INIT(OSDEFCON)
diff --git a/modules/core/os_global.cpp b/modules/core/os_global.cpp
new file mode 100644
index 000000000..a5abde2ff
--- /dev/null
+++ b/modules/core/os_global.cpp
@@ -0,0 +1,62 @@
+/* OperServ 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 CommandOSGlobal : public Command
+{
+ public:
+ CommandOSGlobal() : Command("GLOBAL", 1, 1, "operserv/global")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *msg = params[0].c_str();
+
+ if (Config.WallOSGlobal)
+ ircdproto->SendGlobops(OperServ, "\2%s\2 just used GLOBAL command.", u->nick.c_str());
+ oper_global(const_cast<char *>(u->nick.c_str()), "%s", msg);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_GLOBAL, Config.s_GlobalNoticer);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "GLOBAL", OPER_GLOBAL_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_GLOBAL);
+ }
+};
+
+class OSGlobal : public Module
+{
+ public:
+ OSGlobal(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSGlobal());
+ }
+};
+
+MODULE_INIT(OSGlobal)
diff --git a/modules/core/os_help.cpp b/modules/core/os_help.cpp
new file mode 100644
index 000000000..d88119bd2
--- /dev/null
+++ b/modules/core/os_help.cpp
@@ -0,0 +1,51 @@
+/* OperServ 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 CommandOSHelp : public Command
+{
+ public:
+ CommandOSHelp() : Command("HELP", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ mod_help_cmd(OperServ, u, params[0].c_str());
+ return MOD_CONT;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP);
+ for (CommandMap::const_iterator it = OperServ->Commands.begin(), it_end = OperServ->Commands.end(); it != it_end; ++it)
+ if (!Config.HidePrivilegedCommands || it->second->permission.empty() || (u->Account() && u->Account()->HasCommand(it->second->permission)))
+ it->second->OnServHelp(u);
+ notice_help(Config.s_OperServ, u, OPER_HELP_LOGGED);
+ }
+};
+
+class OSHelp : public Module
+{
+ public:
+ OSHelp(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSHelp());
+ }
+};
+
+MODULE_INIT(OSHelp)
diff --git a/modules/core/os_ignore.cpp b/modules/core/os_ignore.cpp
new file mode 100644
index 000000000..881f79601
--- /dev/null
+++ b/modules/core/os_ignore.cpp
@@ -0,0 +1,203 @@
+/* OperServ 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 CommandOSIgnore : public Command
+{
+ private:
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params)
+ {
+ const char *time = params.size() > 1 ? params[1].c_str() : NULL;
+ const char *nick = params.size() > 2 ? params[2].c_str() : NULL;
+ time_t t;
+
+ if (!time || !nick)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+ else
+ {
+ t = dotime(time);
+
+ if (t <= -1)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_VALID_TIME);
+ return MOD_CONT;
+ }
+ else if (!t)
+ {
+ add_ignore(nick, t);
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_PERM_DONE, nick);
+ }
+ else
+ {
+ add_ignore(nick, t);
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_TIME_DONE, nick, time);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u)
+ {
+ IgnoreData *id;
+
+ if (!ignore)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_LIST);
+ for (id = ignore; id; id = id->next)
+ u->SendMessage(Config.s_OperServ, "%s", id->mask);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params.size() > 1 ? params[1].c_str() : NULL;
+ if (!nick)
+ this->OnSyntaxError(u, "DEL");
+ else
+ {
+ if (delete_ignore(nick))
+ {
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_DEL_DONE, nick);
+ return MOD_CONT;
+ }
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_LIST_NOMATCH, nick);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoClear(User *u)
+ {
+ clear_ignores();
+ notice_lang(Config.s_OperServ, u, OPER_IGNORE_LIST_CLEARED);
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSIgnore() : Command("IGNORE", 1, 3, "operserv/ignore")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (cmd == "ADD")
+ return this->DoAdd(u, params);
+ else if (cmd == "LIST")
+ return this->DoList(u);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params);
+ else if (cmd == "CLEAR")
+ return this->DoClear(u);
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_IGNORE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "IGNORE", OPER_IGNORE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_IGNORE);
+ }
+};
+
+class OSIgnore : public Module
+{
+ public:
+ OSIgnore(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSIgnore());
+
+ Implementation i[] = { I_OnDatabaseRead, I_OnDatabaseWrite };
+ ModuleManager::Attach(i, this, 2);
+ }
+
+ EventReturn OnDatabaseRead(const std::vector<std::string> &params)
+ {
+ std::string buf;
+
+ if (params[0] == "OS" && params.size() >= 4 && params[1] == "IGNORE")
+ {
+ IgnoreData *ign = new IgnoreData;
+ ign->mask = sstrdup(params[2].c_str());
+ ign->time = strtol(params[3].c_str(), NULL, 10);
+ ign->prev = NULL;
+ ign->next = ignore;
+ if (ignore)
+ ignore->prev = ign;
+ ignore = ign;
+
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnDatabaseWrite(void (*Write)(const std::string &))
+ {
+ IgnoreData *ign, *next;
+ time_t now = time(NULL);
+
+ for (ign = ignore; ign; ign = next)
+ {
+ next = ign->next;
+
+ if (ign->time && ign->time <= now)
+ {
+ Alog(LOG_DEBUG) << "[os_ignore] Expiring ignore entry " << ign->mask;
+ if (ign->prev)
+ ign->prev->next = ign->next;
+ else if (ignore == ign)
+ ignore = ign->next;
+ if (ign->next)
+ ign->next->prev = ign->prev;
+ delete [] ign->mask;
+ delete ign;
+ ign = NULL;
+ }
+ else
+ {
+ std::stringstream buf;
+ buf << "OS IGNORE " << ign->mask << " " << ign->time;
+ Write(buf.str());
+ }
+ }
+ }
+};
+
+MODULE_INIT(OSIgnore)
diff --git a/modules/core/os_jupe.cpp b/modules/core/os_jupe.cpp
new file mode 100644
index 000000000..341e2b28d
--- /dev/null
+++ b/modules/core/os_jupe.cpp
@@ -0,0 +1,78 @@
+/* OperServ 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 CommandOSJupe : public Command
+{
+ public:
+ CommandOSJupe() : Command("JUPE", 1, 2, "operserv/jupe")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *jserver = params[0].c_str();
+ const char *reason = params.size() > 1 ? params[1].c_str() : NULL;
+ Server *server = Server::Find(jserver);
+
+ if (!isValidHost(jserver, 3))
+ notice_lang(Config.s_OperServ, u, OPER_JUPE_HOST_ERROR);
+ else if (server && (server == Me || server == Me->GetUplink()))
+ notice_lang(Config.s_OperServ, u, OPER_JUPE_INVALID_SERVER);
+ else
+ {
+ char rbuf[256];
+ snprintf(rbuf, sizeof(rbuf), "Juped by %s%s%s", u->nick.c_str(), reason ? ": " : "", reason ? reason : "");
+ if (server)
+ ircdproto->SendSquit(jserver, rbuf);
+ Server *juped_server = new Server(Me, jserver, 1, rbuf, ircd->ts6 ? ts6_sid_retrieve() : "");
+ juped_server->SetFlag(SERVER_JUPED);
+ ircdproto->SendServer(juped_server);
+
+ if (Config.WallOSJupe)
+ ircdproto->SendGlobops(OperServ, "\2%s\2 used JUPE on \2%s\2", u->nick.c_str(), jserver);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_JUPE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "JUPE", OPER_JUPE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_JUPE);
+ }
+};
+
+class OSJupe : public Module
+{
+ public:
+ OSJupe(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSJupe());
+ }
+};
+
+MODULE_INIT(OSJupe)
diff --git a/modules/core/os_kick.cpp b/modules/core/os_kick.cpp
new file mode 100644
index 000000000..2600814f3
--- /dev/null
+++ b/modules/core/os_kick.cpp
@@ -0,0 +1,80 @@
+/* OperServ 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 CommandOSKick : public Command
+{
+ public:
+ CommandOSKick() : Command("KICK", 3, 3, "operserv/kick")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str(), *nick = params[1].c_str(), *s = params[2].c_str();
+ Channel *c;
+ User *u2;
+
+ if (!(c = findchan(chan)))
+ {
+ notice_lang(Config.s_OperServ, u, CHAN_X_NOT_IN_USE, chan);
+ return MOD_CONT;
+ }
+ else if (c->bouncy_modes)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_BOUNCY_MODES_U_LINE);
+ return MOD_CONT;
+ }
+ else if (!(u2 = finduser(nick)))
+ {
+ notice_lang(Config.s_OperServ, u, NICK_X_NOT_IN_USE, nick);
+ return MOD_CONT;
+ }
+
+ c->Kick(OperServ, u2, "%s (%s)", u->nick.c_str(), s);
+ if (Config.WallOSKick)
+ ircdproto->SendGlobops(OperServ, "%s used KICK on %s/%s", u->nick.c_str(), u2->nick.c_str(), chan);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_KICK);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "KICK", OPER_KICK_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_KICK);
+ }
+};
+
+class OSKick : public Module
+{
+ public:
+ OSKick(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSKick());
+ }
+};
+
+MODULE_INIT(OSKick)
diff --git a/modules/core/os_mode.cpp b/modules/core/os_mode.cpp
new file mode 100644
index 000000000..3f33257fe
--- /dev/null
+++ b/modules/core/os_mode.cpp
@@ -0,0 +1,71 @@
+/* OperServ 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 CommandOSMode : public Command
+{
+ public:
+ CommandOSMode() : Command("MODE", 2, 2, "operserv/mode")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *chan = params[0].c_str(), *modes = params[1].c_str();
+ Channel *c;
+
+ if (!(c = findchan(chan)))
+ notice_lang(Config.s_OperServ, u, CHAN_X_NOT_IN_USE, chan);
+ else if (c->bouncy_modes)
+ notice_lang(Config.s_OperServ, u, OPER_BOUNCY_MODES_U_LINE);
+ else
+ {
+ c->SetModes(OperServ, false, modes);
+
+ if (Config.WallOSMode)
+ ircdproto->SendGlobops(OperServ, "%s used MODE %s on %s", u->nick.c_str(), modes, chan);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_MODE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "MODE", OPER_MODE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_MODE);
+ }
+};
+
+class OSMode : public Module
+{
+ public:
+ OSMode(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSMode());
+ }
+};
+
+MODULE_INIT(OSMode)
diff --git a/modules/core/os_modinfo.cpp b/modules/core/os_modinfo.cpp
new file mode 100644
index 000000000..d71f146c7
--- /dev/null
+++ b/modules/core/os_modinfo.cpp
@@ -0,0 +1,100 @@
+/* OperServ 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"
+
+static int showModuleCmdLoaded(BotInfo *bi, const ci::string &mod_name, User *u);
+
+class CommandOSModInfo : public Command
+{
+ public:
+ CommandOSModInfo() : Command("MODINFO", 1, 1)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const std::string file = params[0].c_str();
+ struct tm tm;
+ char timebuf[64];
+
+ Module *m = FindModule(file.c_str());
+ if (m)
+ {
+ tm = *localtime(&m->created);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, &tm);
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_INFO_LIST, m->name.c_str(), !m->version.empty() ? m->version.c_str() : "?", !m->author.empty() ? m->author.c_str() : "?", timebuf);
+
+ showModuleCmdLoaded(HostServ, m->name.c_str(), u);
+ showModuleCmdLoaded(OperServ, m->name.c_str(), u);
+ showModuleCmdLoaded(NickServ, m->name.c_str(), u);
+ showModuleCmdLoaded(ChanServ, m->name.c_str(), u);
+ showModuleCmdLoaded(BotServ, m->name.c_str(), u);
+ showModuleCmdLoaded(MemoServ, m->name.c_str(), u);
+ }
+ else
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_NO_INFO, file.c_str());
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_MODINFO);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "MODINFO", OPER_MODULE_INFO_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_MODINFO);
+ }
+};
+
+class OSModInfo : public Module
+{
+ public:
+ OSModInfo(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSModInfo());
+ }
+};
+
+static int showModuleCmdLoaded(BotInfo *bi, const ci::string &mod_name, User *u)
+{
+ if (!bi)
+ return 0;
+
+ int display = 0;
+
+ for (std::map<ci::string, Command *>::iterator it = bi->Commands.begin(), it_end = bi->Commands.end(); it != it_end; ++it)
+ {
+ Command *c = it->second;
+
+ if (c->module && c->module->name == mod_name)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_CMD_LIST, c->service, c->name.c_str());
+ ++display;
+ }
+ }
+ return display;
+}
+
+MODULE_INIT(OSModInfo)
diff --git a/modules/core/os_modlist.cpp b/modules/core/os_modlist.cpp
new file mode 100644
index 000000000..8764188da
--- /dev/null
+++ b/modules/core/os_modlist.cpp
@@ -0,0 +1,230 @@
+/* OperServ 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 CommandOSModList : public Command
+{
+ public:
+ CommandOSModList() : Command("MODLIST", 0, 1, "operserv/modlist")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ int count = 0;
+ int showCore = 0;
+ int showThird = 1;
+ int showProto = 1;
+ int showEnc = 1;
+ int showSupported = 1;
+ int showQA = 1;
+ int showDB = 1;
+ int showSocketEngine = 1;
+
+ ci::string param = params.size() ? params[0] : "";
+
+ char core[] = "Core";
+ char third[] = "3rd";
+ char proto[] = "Protocol";
+ char enc[] = "Encryption";
+ char supported[] = "Supported";
+ char qa[] = "QATested";
+ char db[] = "Database";
+ char socketengine[] = "SocketEngine";
+
+ if (!param.empty())
+ {
+ if (param == core)
+ {
+ showCore = 1;
+ showThird = 0;
+ showProto = 0;
+ showEnc = 0;
+ showSupported = 0;
+ showQA = 0;
+ showDB = 0;
+ showSocketEngine = 0;
+ }
+ else if (param == third)
+ {
+ showCore = 0;
+ showThird = 1;
+ showSupported = 0;
+ showQA = 0;
+ showProto = 0;
+ showEnc = 0;
+ showDB = 0;
+ showSocketEngine = 0;
+ }
+ else if (param == proto)
+ {
+ showCore = 0;
+ showThird = 0;
+ showProto = 1;
+ showEnc = 0;
+ showSupported = 0;
+ showQA = 0;
+ showDB = 0;
+ showSocketEngine = 0;
+ }
+ else if (param == supported)
+ {
+ showCore = 0;
+ showThird = 0;
+ showProto = 0;
+ showSupported = 1;
+ showEnc = 0;
+ showQA = 0;
+ showDB = 0;
+ showSocketEngine = 0;
+ }
+ else if (param == qa)
+ {
+ showCore = 0;
+ showThird = 0;
+ showProto = 0;
+ showSupported = 0;
+ showEnc = 0;
+ showQA = 1;
+ showDB = 0;
+ showSocketEngine = 0;
+ }
+ else if (param == enc)
+ {
+ showCore = 0;
+ showThird = 0;
+ showProto = 0;
+ showSupported = 0;
+ showEnc = 1;
+ showQA = 0;
+ showDB = 0;
+ showSocketEngine = 0;
+ }
+ else if (param == db)
+ {
+ showCore = 0;
+ showThird = 0;
+ showProto = 0;
+ showSupported = 0;
+ showEnc = 0;
+ showQA = 0;
+ showDB = 1;
+ showSocketEngine = 0;
+ }
+ else if (param == socketengine)
+ {
+ showCore = showThird = showProto = showSupported = showEnc = showQA = showDB = 0;
+ showSocketEngine = 1;
+ }
+ }
+
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST_HEADER);
+
+ for (std::list<Module *>::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it)
+ {
+ Module *m = *it;
+
+ switch (m->type)
+ {
+ case CORE:
+ if (showCore)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), core);
+ ++count;
+ }
+ break;
+ case THIRD:
+ if (showThird)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), third);
+ ++count;
+ }
+ break;
+ case PROTOCOL:
+ if (showProto)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), proto);
+ ++count;
+ }
+ break;
+ case SUPPORTED:
+ if (showSupported)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), supported);
+ ++count;
+ }
+ break;
+ case QATESTED:
+ if (showQA)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), qa);
+ ++count;
+ }
+ break;
+ case ENCRYPTION:
+ if (showEnc)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), enc);
+ ++count;
+ }
+ break;
+ case DATABASE:
+ if (showDB)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), db);
+ ++count;
+ }
+ break;
+ case SOCKETENGINE:
+ if (showSocketEngine)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST, m->name.c_str(), m->version.c_str(), socketengine);
+ ++count;
+ }
+ break;
+ }
+ }
+ if (!count)
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_NO_LIST);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LIST_FOOTER, count);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_MODLIST);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_MODLIST);
+ }
+};
+
+class OSModList : public Module
+{
+ public:
+ OSModList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSModList());
+ }
+};
+
+MODULE_INIT(OSModList)
diff --git a/modules/core/os_modload.cpp b/modules/core/os_modload.cpp
new file mode 100644
index 000000000..f70c80c05
--- /dev/null
+++ b/modules/core/os_modload.cpp
@@ -0,0 +1,73 @@
+/* OperServ 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 CommandOSModLoad : public Command
+{
+ public:
+ CommandOSModLoad() : Command("MODLOAD", 1, 1, "operserv/modload")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const std::string mname = params[0].c_str();
+
+ Module *m = FindModule(mname);
+ if (m)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_ALREADY_LOADED, mname.c_str());
+ return MOD_CONT;
+ }
+
+ int status = ModuleManager::LoadModule(mname, u);
+ if (status != MOD_ERR_OK)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_LOAD_FAIL, mname.c_str());
+ }
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_MODLOAD);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "MODLOAD", OPER_MODULE_LOAD_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_MODLOAD);
+ }
+};
+
+class OSModLoad : public Module
+{
+ public:
+ OSModLoad(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->SetPermanent(true);
+
+ this->AddCommand(OperServ, new CommandOSModLoad());
+ }
+};
+
+MODULE_INIT(OSModLoad)
diff --git a/modules/core/os_modunload.cpp b/modules/core/os_modunload.cpp
new file mode 100644
index 000000000..0988d4473
--- /dev/null
+++ b/modules/core/os_modunload.cpp
@@ -0,0 +1,75 @@
+/* OperServ 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 CommandOSModUnLoad : public Command
+{
+ public:
+ CommandOSModUnLoad() : Command("MODUNLOAD", 1, 1, "operserv/modload")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const std::string mname = params[0].c_str();
+ int status;
+
+ Module *m = FindModule(mname);
+ if (!m)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_ISNT_LOADED, mname.c_str());
+ return MOD_CONT;
+ }
+
+ Alog() << "Trying to unload module [" << mname << "]";
+
+ status = ModuleManager::UnloadModule(m, u);
+
+ if (status != MOD_ERR_OK)
+ notice_lang(Config.s_OperServ, u, OPER_MODULE_REMOVE_FAIL, mname.c_str());
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_MODUNLOAD);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "MODUNLOAD", OPER_MODULE_UNLOAD_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_MODUNLOAD);
+ }
+};
+
+class OSModUnLoad : public Module
+{
+ public:
+ OSModUnLoad(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->SetPermanent(true);
+
+ this->AddCommand(OperServ, new CommandOSModUnLoad());
+ }
+};
+
+MODULE_INIT(OSModUnLoad)
diff --git a/modules/core/os_news.cpp b/modules/core/os_news.cpp
new file mode 100644
index 000000000..c3ebabf3a
--- /dev/null
+++ b/modules/core/os_news.cpp
@@ -0,0 +1,476 @@
+/* OperServ core functions
+ *
+ * (C) 2003-2010 Anope Team
+ * Contact us at info@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"
+
+#define lenof(a) (sizeof(a) / sizeof(*(a)))
+
+/* List of messages for each news type. This simplifies message sending. */
+
+enum
+{
+ MSG_SYNTAX,
+ MSG_LIST_HEADER,
+ MSG_LIST_ENTRY,
+ MSG_LIST_NONE,
+ MSG_ADD_SYNTAX,
+ MSG_ADD_FULL,
+ MSG_ADDED,
+ MSG_DEL_SYNTAX,
+ MSG_DEL_NOT_FOUND,
+ MSG_DELETED,
+ MSG_DEL_NONE,
+ MSG_DELETED_ALL
+};
+
+struct newsmsgs msgarray[] = {
+ {NEWS_LOGON, "LOGON",
+ {NEWS_LOGON_SYNTAX,
+ NEWS_LOGON_LIST_HEADER,
+ NEWS_LOGON_LIST_ENTRY,
+ NEWS_LOGON_LIST_NONE,
+ NEWS_LOGON_ADD_SYNTAX,
+ NEWS_LOGON_ADD_FULL,
+ NEWS_LOGON_ADDED,
+ NEWS_LOGON_DEL_SYNTAX,
+ NEWS_LOGON_DEL_NOT_FOUND,
+ NEWS_LOGON_DELETED,
+ NEWS_LOGON_DEL_NONE,
+ NEWS_LOGON_DELETED_ALL}
+ },
+ {NEWS_OPER, "OPER",
+ {NEWS_OPER_SYNTAX,
+ NEWS_OPER_LIST_HEADER,
+ NEWS_OPER_LIST_ENTRY,
+ NEWS_OPER_LIST_NONE,
+ NEWS_OPER_ADD_SYNTAX,
+ NEWS_OPER_ADD_FULL,
+ NEWS_OPER_ADDED,
+ NEWS_OPER_DEL_SYNTAX,
+ NEWS_OPER_DEL_NOT_FOUND,
+ NEWS_OPER_DELETED,
+ NEWS_OPER_DEL_NONE,
+ NEWS_OPER_DELETED_ALL}
+ },
+ {NEWS_RANDOM, "RANDOM",
+ {NEWS_RANDOM_SYNTAX,
+ NEWS_RANDOM_LIST_HEADER,
+ NEWS_RANDOM_LIST_ENTRY,
+ NEWS_RANDOM_LIST_NONE,
+ NEWS_RANDOM_ADD_SYNTAX,
+ NEWS_RANDOM_ADD_FULL,
+ NEWS_RANDOM_ADDED,
+ NEWS_RANDOM_DEL_SYNTAX,
+ NEWS_RANDOM_DEL_NOT_FOUND,
+ NEWS_RANDOM_DELETED,
+ NEWS_RANDOM_DEL_NONE,
+ NEWS_RANDOM_DELETED_ALL}
+ }
+};
+
+static void DisplayNews(User *u, NewsType Type)
+{
+ int msg;
+ static unsigned current_news = 0;
+
+ if (Type == NEWS_LOGON)
+ msg = NEWS_LOGON_TEXT;
+ else if (Type == NEWS_OPER)
+ msg = NEWS_OPER_TEXT;
+ else if (Type == NEWS_RANDOM)
+ msg = NEWS_RANDOM_TEXT;
+ else
+ {
+ Alog() << "news: Invalid type (" << Type << ") to display_news()";
+ return;
+ }
+
+ unsigned displayed = 0;
+ bool NewsExists = false;
+ for (unsigned i = 0, end = News.size(); i < end; ++i)
+ {
+ if (News[i]->type == Type)
+ {
+ tm *tm;
+ char timebuf[64];
+
+ NewsExists = true;
+
+ if (Type == NEWS_RANDOM && i == current_news)
+ continue;
+
+ tm = localtime(&News[i]->time);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, tm);
+ notice_lang(Config.s_GlobalNoticer, u, msg, timebuf, News[i]->Text.c_str());
+
+ ++displayed;
+
+ if (Type == NEWS_RANDOM)
+ {
+ current_news = i;
+ return;
+ }
+ else if (displayed >= Config.NewsCount)
+ return;
+ }
+
+ /* Reset to head of list to get first random news value */
+ if (i + 1 == News.size() && Type == NEWS_RANDOM && NewsExists)
+ i = 0;
+ }
+}
+
+static int add_newsitem(User *u, const char *text, NewsType type)
+{
+ int num = 0;
+
+ for (unsigned i = News.size(); i > 0; --i)
+ if (News[i - 1]->type == type)
+ {
+ num = News[i - 1]->num;
+ break;
+ }
+
+ NewsItem *news = new NewsItem;
+ news->type = type;
+ news->num = num + 1;
+ news->Text = text;
+ news->time = time(NULL);
+ news->who = u->nick;
+
+ News.push_back(news);
+
+ return num + 1;
+}
+
+static int del_newsitem(unsigned num, NewsType type)
+{
+ int count = 0;
+
+ for (unsigned i = News.size(); i > 0; --i)
+ if (News[i - 1]->type == type && (num == 0 || News[i - 1]->num == num))
+ {
+ delete News[i - 1];
+ News.erase(News.begin() + i - 1);
+ ++count;
+ }
+
+ return count;
+}
+
+static int *findmsgs(NewsType type, const char **type_name)
+{
+ for (unsigned i = 0; i < lenof(msgarray); ++i)
+ if (msgarray[i].type == type)
+ {
+ if (type_name)
+ *type_name = msgarray[i].name;
+ return msgarray[i].msgs;
+ }
+ return NULL;
+}
+
+class NewsBase : public Command
+{
+ protected:
+ CommandReturn DoList(User *u, NewsType type, int *msgs)
+ {
+ int count = 0;
+ char timebuf[64];
+ struct tm *tm;
+
+ for (unsigned i = 0, end = News.size(); i < end; ++i)
+ if (News[i]->type == type)
+ {
+ if (!count)
+ notice_lang(Config.s_OperServ, u, msgs[MSG_LIST_HEADER]);
+ tm = localtime(&News[i]->time);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_OperServ, u, msgs[MSG_LIST_ENTRY], News[i]->num, timebuf, !News[i]->who.empty() ? News[i]->who.c_str() : "<unknown>", News[i]->Text.c_str());
+ ++count;
+ }
+ if (!count)
+ notice_lang(Config.s_OperServ, u, msgs[MSG_LIST_NONE]);
+ else
+ notice_lang(Config.s_OperServ, u, END_OF_ANY_LIST, "News");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params, NewsType type, int *msgs)
+ {
+ const char *text = params.size() > 1 ? params[1].c_str() : NULL;
+ int n;
+
+ if (!text)
+ this->OnSyntaxError(u, "ADD");
+ else
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+ n = add_newsitem(u, text, type);
+ if (n < 0)
+ notice_lang(Config.s_OperServ, u, msgs[MSG_ADD_FULL]);
+ else
+ notice_lang(Config.s_OperServ, u, msgs[MSG_ADDED], n);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params, NewsType type, int *msgs)
+ {
+ const char *text = params.size() > 1 ? params[1].c_str() : NULL;
+ unsigned num;
+
+ if (!text)
+ this->OnSyntaxError(u, "DEL");
+ else
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+ if (stricmp(text, "ALL"))
+ {
+ num = atoi(text);
+ if (num > 0 && del_newsitem(num, type))
+ {
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DELETED], num);
+ for (unsigned i = 0, end = News.size(); i < end; ++i)
+ if (News[i]->type == type && News[i]->num > num)
+ --News[i]->num;
+ }
+ else
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DEL_NOT_FOUND], num);
+ }
+ else
+ {
+ if (del_newsitem(0, type))
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DELETED_ALL]);
+ else
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DEL_NONE]);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoNews(User *u, const std::vector<ci::string> &params, NewsType type)
+ {
+ ci::string cmd = params[0];
+ const char *type_name;
+ int *msgs;
+
+ msgs = findmsgs(type, &type_name);
+ if (!msgs)
+ {
+ Alog() << "news: Invalid type to do_news()";
+ return MOD_CONT;
+ }
+
+ if (cmd == "LIST")
+ return this->DoList(u, type, msgs);
+ else if (cmd == "ADD")
+ return this->DoAdd(u, params, type, msgs);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params, type, msgs);
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+ public:
+ NewsBase(const ci::string &newstype) : Command(newstype, 1, 2, "operserv/news")
+ {
+ }
+
+ virtual ~NewsBase()
+ {
+ }
+
+ virtual CommandReturn Execute(User *u, const std::vector<ci::string> &params) = 0;
+
+ virtual bool OnHelp(User *u, const ci::string &subcommand) = 0;
+
+ virtual void OnSyntaxError(User *u, const ci::string &subcommand) = 0;
+};
+
+class CommandOSLogonNews : public NewsBase
+{
+ public:
+ CommandOSLogonNews() : NewsBase("LOGONNEWS")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoNews(u, params, NEWS_LOGON);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, NEWS_HELP_LOGON, Config.NewsCount);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "LOGONNEWS", NEWS_LOGON_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_LOGONNEWS);
+ }
+};
+
+class CommandOSOperNews : public NewsBase
+{
+ public:
+ CommandOSOperNews() : NewsBase("OPERNEWS")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoNews(u, params, NEWS_OPER);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, NEWS_HELP_OPER, Config.NewsCount);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "OPERNEWS", NEWS_OPER_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_OPERNEWS);
+ }
+};
+
+class CommandOSRandomNews : public NewsBase
+{
+ public:
+ CommandOSRandomNews() : NewsBase("RANDOMNEWS")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoNews(u, params, NEWS_RANDOM);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, NEWS_HELP_RANDOM);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "RANDOMNEWS", NEWS_RANDOM_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_RANDOMNEWS);
+ }
+};
+
+class OSNews : public Module
+{
+ public:
+ OSNews(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSLogonNews());
+ this->AddCommand(OperServ, new CommandOSOperNews());
+ this->AddCommand(OperServ, new CommandOSRandomNews());
+
+ Implementation i[] = { I_OnUserModeSet, I_OnUserConnect, I_OnDatabaseRead, I_OnDatabaseWrite };
+ ModuleManager::Attach(i, this, 4);
+ }
+
+ ~OSNews()
+ {
+ for (std::vector<NewsItem *>::iterator it = News.begin(), it_end = News.end(); it != it_end; ++it)
+ delete *it;
+ News.clear();
+ }
+
+ void OnUserModeSet(User *u, UserModeName Name)
+ {
+ if (Name == UMODE_OPER)
+ DisplayNews(u, NEWS_OPER);
+ }
+
+ void OnUserConnect(User *u)
+ {
+ DisplayNews(u, NEWS_LOGON);
+ DisplayNews(u, NEWS_RANDOM);
+ }
+
+ EventReturn OnDatabaseRead(const std::vector<std::string> &params)
+ {
+ if (params[0] == "OS" && params.size() >= 7 && params[1] == "NEWS")
+ {
+ NewsItem *n = new NewsItem;
+ n->num = atoi(params[2].c_str());
+ n->time = strtol(params[3].c_str(), NULL, 10);
+ n->who = params[4];
+ if (params[5] == "LOGON")
+ n->type = NEWS_LOGON;
+ else if (params[5] == "RANDOM")
+ n->type = NEWS_RANDOM;
+ else if (params[5] == "OPER")
+ n->type = NEWS_OPER;
+ n->Text = params[6];
+ News.push_back(n);
+
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnDatabaseWrite(void (*Write)(const std::string &))
+ {
+ for (std::vector<NewsItem *>::iterator it = News.begin(); it != News.end(); ++it)
+ {
+ NewsItem *n = *it;
+ char buf[512]; const char* ntype;
+ if (n->type == NEWS_LOGON)
+ ntype = "LOGON";
+ else if (n->type == NEWS_RANDOM)
+ ntype = "RANDOM";
+ else if (n->type == NEWS_OPER)
+ ntype = "OPER";
+ snprintf(buf, sizeof(buf), "OS NEWS %d %ld %s %s :%s", n->num, n->time, n->who.c_str(), ntype, n->Text.c_str());
+ Write(buf);
+ }
+ }
+};
+
+MODULE_INIT(OSNews)
diff --git a/modules/core/os_noop.cpp b/modules/core/os_noop.cpp
new file mode 100644
index 000000000..51337106e
--- /dev/null
+++ b/modules/core/os_noop.cpp
@@ -0,0 +1,89 @@
+/* OperServ 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 CommandOSNOOP : public Command
+{
+ public:
+ CommandOSNOOP() : Command("NOOP", 2, 2, "operserv/noop")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+ const char *server = params[1].c_str();
+
+ if (cmd == "SET")
+ {
+ std::string reason;
+
+ /* Remove the O:lines */
+ ircdproto->SendSVSNOOP(server, 1);
+
+ reason = "NOOP command used by " + u->nick;
+ if (Config.WallOSNoOp)
+ ircdproto->SendGlobops(OperServ, "\2%s\2 used NOOP on \2%s\2", u->nick.c_str(), server);
+ notice_lang(Config.s_OperServ, u, OPER_NOOP_SET, server);
+
+ /* Kill all the IRCops of the server */
+ for (user_map::const_iterator it = UserListByNick.begin(), it_end = UserListByNick.end(); it != it_end; )
+ {
+ User *u2 = it->second;
+ ++it;
+
+ if (u2 && is_oper(u2) && Anope::Match(u2->server->GetName(), server, true))
+ kill_user(Config.s_OperServ, u2->nick.c_str(), reason.c_str());
+ }
+ }
+ else if (cmd == "REVOKE")
+ {
+ ircdproto->SendSVSNOOP(server, 0);
+ notice_lang(Config.s_OperServ, u, OPER_NOOP_REVOKE, server);
+ }
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_NOOP);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "NOOP", OPER_NOOP_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_NOOP);
+ }
+};
+
+class OSNOOP : public Module
+{
+ public:
+ OSNOOP(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSNOOP());
+ }
+};
+
+MODULE_INIT(OSNOOP)
diff --git a/modules/core/os_oline.cpp b/modules/core/os_oline.cpp
new file mode 100644
index 000000000..bceea1a49
--- /dev/null
+++ b/modules/core/os_oline.cpp
@@ -0,0 +1,83 @@
+/* OperServ 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 CommandOSOLine : public Command
+{
+ public:
+ CommandOSOLine() : Command("OLINE", 2, 2, "operserv/oline")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ const char *flag = params[1].c_str();
+ User *u2 = NULL;
+
+ /* let's check whether the user is online */
+ if (!(u2 = finduser(nick)))
+ notice_lang(Config.s_OperServ, u, NICK_X_NOT_IN_USE, nick);
+ else if (u2 && flag[0] == '+')
+ {
+ ircdproto->SendSVSO(Config.s_OperServ, nick, flag);
+ u2->SetMode(OperServ, UMODE_OPER);
+ notice_lang(Config.s_OperServ, u2, OPER_OLINE_IRCOP);
+ notice_lang(Config.s_OperServ, u, OPER_OLINE_SUCCESS, flag, nick);
+ ircdproto->SendGlobops(OperServ, "\2%s\2 used OLINE for %s", u->nick.c_str(), nick);
+ }
+ else if (u2 && flag[0] == '-')
+ {
+ ircdproto->SendSVSO(Config.s_OperServ, nick, flag);
+ notice_lang(Config.s_OperServ, u, OPER_OLINE_SUCCESS, flag, nick);
+ ircdproto->SendGlobops(OperServ, "\2%s\2 used OLINE for %s", u->nick.c_str(), nick);
+ }
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_OLINE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "OLINE", OPER_OLINE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_OLINE);
+ }
+};
+
+class OSOLine : public Module
+{
+ public:
+ OSOLine(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!ircd->omode)
+ throw ModuleException("Your IRCd does not support OMODE.");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSOLine());
+ }
+};
+
+MODULE_INIT(OSOLine)
diff --git a/modules/core/os_quit.cpp b/modules/core/os_quit.cpp
new file mode 100644
index 000000000..1c5a073bc
--- /dev/null
+++ b/modules/core/os_quit.cpp
@@ -0,0 +1,62 @@
+/* OperServ 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 CommandOSQuit : public Command
+{
+ public:
+ CommandOSQuit() : Command("QUIT", 0, 0, "operserv/quit")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ quitmsg = new char[28 + u->nick.length()];
+ if (!quitmsg)
+ quitmsg = "QUIT command received, but out of memory!";
+ else
+ sprintf(const_cast<char *>(quitmsg), "QUIT command received from %s", u->nick.c_str()); // XXX we know this is safe, but..
+
+ if (Config.GlobalOnCycle)
+ oper_global(NULL, "%s", Config.GlobalOnCycleMessage);
+ quitting = 1;
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_QUIT);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_QUIT);
+ }
+};
+
+class OSQuit : public Module
+{
+ public:
+ OSQuit(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSQuit());
+ }
+};
+
+MODULE_INIT(OSQuit)
diff --git a/modules/core/os_reload.cpp b/modules/core/os_reload.cpp
new file mode 100644
index 000000000..15a45d0a2
--- /dev/null
+++ b/modules/core/os_reload.cpp
@@ -0,0 +1,64 @@
+/* OperServ 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 CommandOSReload : public Command
+{
+ public:
+ CommandOSReload() : Command("RELOAD", 0, 0, "operserv/reload")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ if (!read_config(1))
+ {
+ quitmsg = new char[28 + u->nick.length()];
+ if (!quitmsg)
+ quitmsg = "Error during the reload of the configuration file, but out of memory!";
+ else
+ sprintf(const_cast<char *>(quitmsg), /* XXX */ "Error during the reload of the configuration file!");
+ quitting = 1;
+ }
+
+ FOREACH_MOD(I_OnReload, OnReload(false));
+ notice_lang(Config.s_OperServ, u, OPER_RELOAD);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_RELOAD);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_RELOAD);
+ }
+};
+
+class OSReload : public Module
+{
+ public:
+ OSReload(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSReload());
+ }
+};
+
+MODULE_INIT(OSReload)
diff --git a/modules/core/os_restart.cpp b/modules/core/os_restart.cpp
new file mode 100644
index 000000000..04c6d02d6
--- /dev/null
+++ b/modules/core/os_restart.cpp
@@ -0,0 +1,62 @@
+/* OperServ 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 CommandOSRestart : public Command
+{
+ public:
+ CommandOSRestart() : Command("RESTART", 0, 0, "operserv/restart")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ quitmsg = new char[31 + u->nick.length()];
+ if (!quitmsg)
+ quitmsg = "RESTART command received, but out of memory!";
+ else
+ sprintf(const_cast<char *>(quitmsg), /* XXX */ "RESTART command received from %s", u->nick.c_str());
+
+ if (Config.GlobalOnCycle)
+ oper_global(NULL, "%s", Config.GlobalOnCycleMessage);
+ /* raise(SIGHUP); */
+ do_restart_services();
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_RESTART);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_RESTART);
+ }
+};
+
+class OSRestart : public Module
+{
+ public:
+ OSRestart(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSRestart());
+ }
+};
+
+MODULE_INIT(OSRestart)
diff --git a/modules/core/os_session.cpp b/modules/core/os_session.cpp
new file mode 100644
index 000000000..4ec73460c
--- /dev/null
+++ b/modules/core/os_session.cpp
@@ -0,0 +1,509 @@
+/* OperServ 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 ExceptionDelCallback : public NumberList
+{
+ protected:
+ User *u;
+ unsigned Deleted;
+ public:
+ ExceptionDelCallback(User *_u, const std::string &numlist) : NumberList(numlist, true), u(_u), Deleted(0)
+ {
+ }
+
+ ~ExceptionDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
+ else if (Deleted == 1)
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_DELETED_ONE);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_DELETED_SEVERAL, Deleted);
+ }
+
+ virtual void HandleNumber(unsigned Number)
+ {
+ if (Number > nexceptions)
+ return;
+
+ ++Deleted;
+
+ DoDel(u, Number - 1);
+ }
+
+ static void DoDel(User *u, unsigned index)
+ {
+ FOREACH_MOD(I_OnExceptionDel, OnExceptionDel(u, &exceptions[index]));
+
+ delete [] exceptions[index].mask;
+ delete [] exceptions[index].reason;
+ --nexceptions;
+ memmove(exceptions + index, exceptions + index + 1, sizeof(Exception) * (nexceptions - index));
+ exceptions = static_cast<Exception *>(srealloc(exceptions, sizeof(Exception) * nexceptions));
+ }
+};
+
+class ExceptionListCallback : public NumberList
+{
+ protected:
+ User *u;
+ bool SentHeader;
+ public:
+ ExceptionListCallback(User *_u, const std::string &numlist) : NumberList(numlist, false), u(_u), SentHeader(false)
+ {
+ }
+
+ virtual void HandleNumber(unsigned Number)
+ {
+ if (Number > nexceptions)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_LIST_COLHEAD);
+ }
+
+ DoList(u, Number - 1);
+ }
+
+ static void DoList(User *u, unsigned index)
+ {
+ if (index >= nexceptions)
+ return;
+
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_LIST_FORMAT, index + 1, exceptions[index].limit, exceptions[index].mask);
+ }
+};
+
+class ExceptionViewCallback : public ExceptionListCallback
+{
+ public:
+ ExceptionViewCallback(User *_u, const std::string &numlist) : ExceptionListCallback(_u, numlist)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ if (Number > nexceptions)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
+ }
+
+ DoList(u, Number - 1);
+ }
+
+ static void DoList(User *u, unsigned index)
+ {
+ if (index >= nexceptions)
+ return;
+
+ char timebuf[32], expirebuf[256];
+ struct tm tm;
+ time_t t = time(NULL);
+
+ tm = *localtime(exceptions[index].time ? &exceptions[index].time : &t);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, &tm);
+
+ expire_left(u->Account(), expirebuf, sizeof(expirebuf), exceptions[index].expires);
+
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_VIEW_FORMAT, index + 1, exceptions[index].mask, exceptions[index].who ? exceptions[index].who : "<unknown>", timebuf, expirebuf, exceptions[index].limit, exceptions[index].reason);
+ }
+};
+
+class CommandOSSession : public Command
+{
+ private:
+ CommandReturn DoList(User *u, const std::vector<ci::string> &params)
+ {
+ int mincount;
+ const char *param = params[1].c_str();
+
+ if ((mincount = atoi(param)) <= 1)
+ notice_lang(Config.s_OperServ, u, OPER_SESSION_INVALID_THRESHOLD);
+ else
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SESSION_LIST_HEADER, mincount);
+ notice_lang(Config.s_OperServ, u, OPER_SESSION_LIST_COLHEAD);
+
+ for (session_map::const_iterator it = SessionList.begin(), it_end = SessionList.end(); it != it_end; ++it)
+ {
+ Session *session = it->second;
+
+ if (session->count >= mincount)
+ notice_lang(Config.s_OperServ, u, OPER_SESSION_LIST_FORMAT, session->count, session->host);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoView(User *u, const std::vector<ci::string> &params)
+ {
+ const char *param = params[1].c_str();
+ Session *session = findsession(param);
+
+ if (!session)
+ notice_lang(Config.s_OperServ, u, OPER_SESSION_NOT_FOUND, param);
+ else {
+ Exception *exception = find_host_exception(param);
+ notice_lang(Config.s_OperServ, u, OPER_SESSION_VIEW_FORMAT, param, session->count, exception ? exception-> limit : Config.DefSessionLimit);
+ }
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSSession() : Command("SESSION", 2, 2, "operserv/session")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (!Config.LimitSessions)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SESSION_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (cmd == "LIST")
+ return this->DoList(u, params);
+ else if (cmd == "VIEW")
+ return this->DoView(u, params);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_SESSION);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "SESSION", OPER_SESSION_LIST_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_SESSION);
+ }
+};
+
+class CommandOSException : public Command
+{
+ private:
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params)
+ {
+ const char *mask, *expiry, *limitstr;
+ char reason[BUFSIZE];
+ unsigned last_param = 3;
+ int x;
+
+ mask = params.size() > 1 ? params[1].c_str() : NULL;
+ if (mask && *mask == '+')
+ {
+ expiry = mask;
+ mask = params.size() > 2 ? params[2].c_str() : NULL;
+ last_param = 4;
+ }
+ else
+ expiry = NULL;
+
+ limitstr = params.size() > last_param - 1 ? params[last_param - 1].c_str() : NULL;
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+ snprintf(reason, sizeof(reason), "%s%s%s", params[last_param].c_str(), last_param == 3 && params.size() > 4 ? " " : "", last_param == 3 && params.size() > 4 ? params[4].c_str() : "");
+
+ if (!*reason)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+
+ time_t expires = expiry ? dotime(expiry) : Config.ExceptionExpiry;
+ if (expires < 0)
+ {
+ notice_lang(Config.s_OperServ, u, BAD_EXPIRY_TIME);
+ return MOD_CONT;
+ }
+ else if (expires > 0)
+ expires += time(NULL);
+
+ int limit = limitstr && isdigit(*limitstr) ? atoi(limitstr) : -1;
+
+ if (limit < 0 || limit > static_cast<int>(Config.MaxSessionLimit))
+ {
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_INVALID_LIMIT, Config.MaxSessionLimit);
+ return MOD_CONT;
+ }
+ else
+ {
+ if (strchr(mask, '!') || strchr(mask, '@'))
+ {
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_INVALID_HOSTMASK);
+ return MOD_CONT;
+ }
+
+ x = exception_add(u, mask, limit, reason, u->nick.c_str(), expires);
+
+ if (x == 1)
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_ADDED, mask, limit);
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params)
+ {
+ const char *mask = params.size() > 1 ? params[1].c_str() : NULL;
+ int i;
+
+ if (!mask)
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask))
+ {
+ ExceptionDelCallback list(u, mask);
+ list.Process();
+ }
+ else
+ {
+ int deleted = 0;
+
+ for (i = 0; i < nexceptions; ++i)
+ if (!stricmp(mask, exceptions[i].mask))
+ {
+ ExceptionDelCallback::DoDel(u, i);
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_DELETED, mask);
+ deleted = 1;
+ break;
+ }
+ if (!deleted && i == nexceptions)
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_NOT_FOUND, mask);
+ }
+
+ /* Renumber the exception list. I don't believe in having holes in
+ * lists - it makes code more complex, harder to debug and we end up
+ * with huge index numbers. Imho, fixed numbering is only beneficial
+ * when one doesn't have range capable manipulation. -TheShadow */
+
+ for (i = 0; i < nexceptions; ++i)
+ exceptions[i].num = i;
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoMove(User *u, const std::vector<ci::string> &params)
+ {
+ Exception *exception;
+ const char *n1str = params.size() > 1 ? params[1].c_str() : NULL; /* From position */
+ const char *n2str = params.size() > 2 ? params[2].c_str() : NULL; /* To position */
+ int n1, n2, i;
+
+ if (!n2str)
+ {
+ this->OnSyntaxError(u, "MOVE");
+ return MOD_CONT;
+ }
+
+ n1 = atoi(n1str) - 1;
+ n2 = atoi(n2str) - 1;
+
+ if (n1 >= 0 && n1 < nexceptions && n2 >= 0 && n2 < nexceptions && n1 != n2)
+ {
+ exception = static_cast<Exception *>(smalloc(sizeof(Exception)));
+ memcpy(exception, &exceptions[n1], sizeof(Exception));
+
+ if (n1 < n2)
+ {
+ /* Shift upwards */
+ memmove(&exceptions[n1], &exceptions[n1 + 1], sizeof(Exception) * (n2 - n1));
+ memmove(&exceptions[n2], exception, sizeof(Exception));
+ }
+ else
+ {
+ /* Shift downwards */
+ memmove(&exceptions[n2 + 1], &exceptions[n2], sizeof(Exception) * (n1 - n2));
+ memmove(&exceptions[n2], exception, sizeof(Exception));
+ }
+
+ free(exception);
+
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_MOVED, exceptions[n1].mask, n1 + 1, n2 + 1);
+
+ /* Renumber the exception list. See DoDel() above for why. */
+ for (i = 0; i < nexceptions; ++i)
+ exceptions[i].num = i;
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+ }
+ else
+ this->OnSyntaxError(u, "MOVE");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, const std::vector<ci::string> &params)
+ {
+ int i;
+ expire_exceptions();
+ const char *mask = params.size() > 1 ? params[1].c_str() : NULL;
+
+ if (mask && strspn(mask, "1234567890,-") == strlen(mask))
+ {
+ ExceptionListCallback list(u, mask);
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (i = 0; i < nexceptions; ++i)
+ if (!mask || Anope::Match(exceptions[i].mask, mask, false))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_LIST_COLHEAD);
+ }
+
+ ExceptionListCallback::DoList(u, i);
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoView(User *u, const std::vector<ci::string> &params)
+ {
+ int i;
+ expire_exceptions();
+ const char *mask = params.size() > 1 ? params[1].c_str() : NULL;
+
+ if (mask && strspn(mask, "1234567890,-") == strlen(mask))
+ {
+ ExceptionViewCallback list(u, mask);
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (i = 0; i < nexceptions; ++i)
+ if (!mask || Anope::Match(exceptions[i].mask, mask, false))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
+ }
+
+ ExceptionViewCallback::DoList(u, i);
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
+ }
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSException() : Command("EXCEPTION", 1, 5)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (!Config.LimitSessions)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_EXCEPTION_DISABLED);
+ return MOD_CONT;
+ }
+
+ if (cmd == "ADD")
+ return this->DoAdd(u, params);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params);
+ else if (cmd == "MOVE")
+ return this->DoMove(u, params);
+ else if (cmd == "LIST")
+ return this->DoList(u, params);
+ else if (cmd == "VIEW")
+ return this->DoView(u, params);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_EXCEPTION);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "EXCEPTION", OPER_EXCEPTION_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_EXCEPTION);
+ }
+};
+
+class OSSession : public Module
+{
+ public:
+ OSSession(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSSession());
+ this->AddCommand(OperServ, new CommandOSException());
+ }
+};
+
+MODULE_INIT(OSSession)
diff --git a/modules/core/os_set.cpp b/modules/core/os_set.cpp
new file mode 100644
index 000000000..06a5f6886
--- /dev/null
+++ b/modules/core/os_set.cpp
@@ -0,0 +1,312 @@
+/* OperServ 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 CommandOSSet : public Command
+{
+ private:
+ CommandReturn DoList(User *u)
+ {
+ int index;
+
+ index = allow_ignore ? OPER_SET_LIST_OPTION_ON : OPER_SET_LIST_OPTION_OFF;
+ notice_lang(Config.s_OperServ, u, index, "IGNORE");
+ index = readonly ? OPER_SET_LIST_OPTION_ON : OPER_SET_LIST_OPTION_OFF;
+ notice_lang(Config.s_OperServ, u, index, "READONLY");
+ index = LogChan ? OPER_SET_LIST_OPTION_ON : OPER_SET_LIST_OPTION_OFF;
+ notice_lang(Config.s_OperServ, u, index, "LOGCHAN");
+ index = debug ? OPER_SET_LIST_OPTION_ON : OPER_SET_LIST_OPTION_OFF;
+ notice_lang(Config.s_OperServ, u, index, "DEBUG");
+ index = noexpire ? OPER_SET_LIST_OPTION_ON : OPER_SET_LIST_OPTION_OFF;
+ notice_lang(Config.s_OperServ, u, index, "NOEXPIRE");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoSetIgnore(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string setting = params.size() > 1 ? params[1] : "";
+
+ if (setting.empty())
+ {
+ this->OnSyntaxError(u, "IGNORE");
+ return MOD_CONT;
+ }
+
+ if (setting == "ON")
+ {
+ allow_ignore = 1;
+ notice_lang(Config.s_OperServ, u, OPER_SET_IGNORE_ON);
+ }
+ else if (setting == "OFF")
+ {
+ allow_ignore = 0;
+ notice_lang(Config.s_OperServ, u, OPER_SET_IGNORE_OFF);
+ }
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SET_IGNORE_ERROR);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoSetReadOnly(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string setting = params.size() > 1 ? params[1] : "";
+
+ if (setting.empty())
+ {
+ this->OnSyntaxError(u, "READONLY");
+ return MOD_CONT;
+ }
+
+ if (setting == "ON")
+ {
+ readonly = 1;
+ Alog() << "Read-only mode activated";
+ close_log();
+ notice_lang(Config.s_OperServ, u, OPER_SET_READONLY_ON);
+ }
+ else if (setting == "OFF")
+ {
+ readonly = 0;
+ open_log();
+ Alog() << "Read-only mode deactivated";
+ notice_lang(Config.s_OperServ, u, OPER_SET_READONLY_OFF);
+ }
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SET_READONLY_ERROR);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoSetLogChan(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string setting = params.size() > 1 ? params[1] : "";
+ Channel *c;
+
+ if (setting.empty())
+ {
+ this->OnSyntaxError(u, "LOGCHAN");
+ return MOD_CONT;
+ }
+
+ /* Unlike the other SET commands where only stricmp is necessary,
+ * we also have to ensure that Config.LogChannel is defined or we can't
+ * send to it.
+ *
+ * -jester
+ */
+ if (Config.LogChannel && setting == "ON")
+ {
+ if (ircd->join2msg)
+ {
+ c = findchan(Config.LogChannel);
+ if (c)
+ Global->Join(c);
+ else
+ Global->Join(Config.LogChannel);
+ }
+ LogChan = true;
+ Alog() << "Now sending log messages to " << Config.LogChannel;
+ notice_lang(Config.s_OperServ, u, OPER_SET_LOGCHAN_ON, Config.LogChannel);
+ }
+ else if (Config.LogChannel && setting == "OFF")
+ {
+ Alog() << "No longer sending log messages to a channel";
+ c = findchan(Config.LogChannel);
+ if (ircd->join2msg && c)
+ Global->Part(c);
+ LogChan = false;
+ notice_lang(Config.s_OperServ, u, OPER_SET_LOGCHAN_OFF);
+ }
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SET_LOGCHAN_ERROR);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoSetSuperAdmin(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string setting = params.size() > 1 ? params[1] : "";
+
+ if (setting.empty())
+ {
+ this->OnSyntaxError(u, "SUPERADMIN");
+ return MOD_CONT;
+ }
+
+ /**
+ * Allow the user to turn super admin on/off
+ *
+ * Rob
+ **/
+ if (!Config.SuperAdmin)
+ notice_lang(Config.s_OperServ, u, OPER_SUPER_ADMIN_NOT_ENABLED);
+ else if (setting == "ON")
+ {
+ u->isSuperAdmin = 1;
+ notice_lang(Config.s_OperServ, u, OPER_SUPER_ADMIN_ON);
+ Alog() << Config.s_OperServ << ": " << u->nick << " is a SuperAdmin";
+ ircdproto->SendGlobops(OperServ, getstring(OPER_SUPER_ADMIN_WALL_ON), u->nick.c_str());
+ }
+ else if (setting == "OFF")
+ {
+ u->isSuperAdmin = 0;
+ notice_lang(Config.s_OperServ, u, OPER_SUPER_ADMIN_OFF);
+ Alog() << Config.s_OperServ << ": " << u->nick << " is no longer a SuperAdmin";
+ ircdproto->SendGlobops(OperServ, getstring(OPER_SUPER_ADMIN_WALL_OFF), u->nick.c_str());
+ }
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SUPER_ADMIN_SYNTAX);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoSetDebug(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string setting = params.size() > 1 ? params[1] : "";
+
+ if (setting.empty())
+ {
+ this->OnSyntaxError(u, "DEBUG");
+ return MOD_CONT;
+ }
+
+ if (setting == "ON")
+ {
+ debug = 1;
+ Alog() << "Debug mode activated";
+ notice_lang(Config.s_OperServ, u, OPER_SET_DEBUG_ON);
+ }
+ else if (setting == "OFF" || (setting[0] == '0' && !atoi(setting.c_str())))
+ {
+ Alog() << "Debug mode deactivated";
+ debug = 0;
+ notice_lang(Config.s_OperServ, u, OPER_SET_DEBUG_OFF);
+ }
+ else if (isdigit(setting[0]) && atoi(setting.c_str()) > 0)
+ {
+ debug = atoi(setting.c_str());
+ Alog() << "Debug mode activated (level " << debug << ")";
+ notice_lang(Config.s_OperServ, u, OPER_SET_DEBUG_LEVEL, debug);
+ }
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SET_DEBUG_ERROR);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoSetNoExpire(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string setting = params.size() > 1 ? params[1] : "";
+
+ if (setting.empty())
+ {
+ this->OnSyntaxError(u, "NOEXPIRE");
+ return MOD_CONT;
+ }
+
+ if (setting == "ON")
+ {
+ noexpire = 1;
+ Alog() << "No expire mode activated";
+ notice_lang(Config.s_OperServ, u, OPER_SET_NOEXPIRE_ON);
+ }
+ else if (setting == "OFF")
+ {
+ noexpire = 0;
+ Alog() << "No expire mode deactivated";
+ notice_lang(Config.s_OperServ, u, OPER_SET_NOEXPIRE_OFF);
+ }
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SET_NOEXPIRE_ERROR);
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSSet() : Command("SET", 1, 2, "operserv/set")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string option = params[0];
+
+ if (option == "LIST")
+ return this->DoList(u);
+ else if (option == "IGNORE")
+ return this->DoSetIgnore(u, params);
+ else if (option == "READONLY")
+ return this->DoSetReadOnly(u, params);
+ else if (option == "LOGCHAN")
+ return this->DoSetLogChan(u, params);
+ else if (option == "SUPERADMIN")
+ return this->DoSetSuperAdmin(u, params);
+ else if (option == "DEBUG")
+ return this->DoSetDebug(u, params);
+ else if (option == "NOEXPIRE")
+ return this->DoSetNoExpire(u, params);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SET_UNKNOWN_OPTION, option.c_str());
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ if (subcommand.empty())
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET);
+ else if (subcommand == "LIST")
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET_LIST);
+ else if (subcommand == "READONLY")
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET_READONLY);
+ else if (subcommand == "LOGCHAN")
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET_LOGCHAN);
+ else if (subcommand == "DEBUG")
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET_DEBUG);
+ else if (subcommand == "NOEXPIRE")
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET_NOEXPIRE);
+ else if (subcommand == "IGNORE")
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET_IGNORE);
+ else if (subcommand == "SUPERADMIN")
+ notice_help(Config.s_OperServ, u, OPER_HELP_SET_SUPERADMIN);
+ else
+ return false;
+
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "SET", OPER_SET_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_SET);
+ }
+};
+
+class OSSet : public Module
+{
+ public:
+ OSSet(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSSet());
+ }
+};
+
+MODULE_INIT(OSSet)
diff --git a/modules/core/os_shutdown.cpp b/modules/core/os_shutdown.cpp
new file mode 100644
index 000000000..acdddac8e
--- /dev/null
+++ b/modules/core/os_shutdown.cpp
@@ -0,0 +1,61 @@
+/* OperServ 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 CommandOSShutdown : public Command
+{
+ public:
+ CommandOSShutdown() : Command("SHUTDOWN", 0, 0, "operserv/shutdown")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ quitmsg = new char[32 + u->nick.length()];
+ if (!quitmsg)
+ quitmsg = "SHUTDOWN command received, but out of memory!";
+ else
+ sprintf(const_cast<char *>(quitmsg), /* XXX */ "SHUTDOWN command received from %s", u->nick.c_str());
+
+ if (Config.GlobalOnCycle)
+ oper_global(NULL, "%s", Config.GlobalOnCycleMessage);
+ shutting_down = 1;
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_SHUTDOWN);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_SHUTDOWN);
+ }
+};
+
+class OSShutdown : public Module
+{
+ public:
+ OSShutdown(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSShutdown());
+ }
+};
+
+MODULE_INIT(OSShutdown)
diff --git a/modules/core/os_snline.cpp b/modules/core/os_snline.cpp
new file mode 100644
index 000000000..6dd252bec
--- /dev/null
+++ b/modules/core/os_snline.cpp
@@ -0,0 +1,431 @@
+/* OperServ 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"
+#include "hashcomp.h"
+
+class SNLineDelCallback : public NumberList
+{
+ User *u;
+ unsigned Deleted;
+ public:
+ SNLineDelCallback(User *_u, const std::string &numlist) : NumberList(numlist, true), u(_u), Deleted(0)
+ {
+ }
+
+ ~SNLineDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_NO_MATCH);
+ else if (Deleted == 0)
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_DELETED_ONE);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_DELETED_SEVERAL, Deleted);
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SNLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ ++Deleted;
+ DoDel(u, x);
+ }
+
+ static void DoDel(User *u, XLine *x)
+ {
+ SNLine->DelXLine(x);
+ }
+};
+
+class SNLineListCallback : public NumberList
+{
+ protected:
+ User *u;
+ bool SentHeader;
+ public:
+ SNLineListCallback(User *_u, const std::string &numlist) : NumberList(numlist, false), u(_u), SentHeader(false)
+ {
+ }
+
+ ~SNLineListCallback()
+ {
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_NO_MATCH);
+ }
+
+ virtual void HandleNumber(unsigned Number)
+ {
+ XLine *x = SNLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_LIST_HEADER);
+ }
+
+ DoList(u, x, Number - 1);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str());
+ }
+};
+
+class SNLineViewCallback : public SNLineListCallback
+{
+ public:
+ SNLineViewCallback(User *_u, const std::string &numlist) : SNLineListCallback(_u, numlist)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SNLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_VIEW_HEADER);
+ }
+
+ DoList(u, x, Number - 1);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ char timebuf[32], expirebuf[256];
+ struct tm tm;
+
+ tm = *localtime(&x->Created);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, &tm);
+ expire_left(u->Account(), expirebuf, sizeof(expirebuf), x->Expires);
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), timebuf, expirebuf, x->Reason.c_str());
+ }
+};
+
+class CommandOSSNLine : public Command
+{
+ private:
+ CommandReturn OnAdd(User *u, const std::vector<ci::string> &params)
+ {
+ unsigned last_param = 2;
+ const char *param, *expiry;
+ char rest[BUFSIZE];
+ time_t expires;
+
+ param = params.size() > 1 ? params[1].c_str() : NULL;
+ if (param && *param == '+')
+ {
+ expiry = param;
+ param = params.size() > 2 ? params[2].c_str() : NULL;
+ last_param = 3;
+ }
+ else
+ expiry = NULL;
+
+ expires = expiry ? dotime(expiry) : Config.SNLineExpiry;
+ /* If the expiry given does not contain a final letter, it's in days,
+ * said the doc. Ah well.
+ */
+ if (expiry && isdigit(expiry[strlen(expiry) - 1]))
+ expires *= 86400;
+ /* Do not allow less than a minute expiry time */
+ if (expires && expires < 60)
+ {
+ notice_lang(Config.s_OperServ, u, BAD_EXPIRY_TIME);
+ return MOD_CONT;
+ }
+ else if (expires > 0)
+ expires += time(NULL);
+
+ if (!param)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+ snprintf(rest, sizeof(rest), "%s%s%s", param, params.size() > last_param ? " " : "", params.size() > last_param ? params[last_param].c_str() : "");
+
+ if (std::string(rest).find(':') == std::string::npos)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+
+ sepstream sep(rest, ':');
+ ci::string mask;
+ sep.GetToken(mask);
+ std::string reason = sep.GetRemaining();
+
+ if (!mask.empty() && !reason.empty())
+ {
+ /* Clean up the last character of the mask if it is a space
+ * See bug #761
+ */
+ unsigned masklen = mask.size();
+ if (mask[masklen - 1] == ' ')
+ mask.erase(masklen - 1);
+
+ XLine *x = SNLine->Add(OperServ, u, mask, expires, reason);
+
+ if (!x)
+ return MOD_CONT;
+
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_ADDED, mask.c_str());
+
+ if (Config.WallOSSNLine)
+ {
+ char buf[128];
+
+ if (!expires)
+ strcpy(buf, "does not expire");
+ else
+ {
+ int wall_expiry = expires - time(NULL);
+ const char *s = NULL;
+
+ if (wall_expiry >= 86400)
+ {
+ wall_expiry /= 86400;
+ s = "day";
+ }
+ else if (wall_expiry >= 3600)
+ {
+ wall_expiry /= 3600;
+ s = "hour";
+ }
+ else if (wall_expiry >= 60)
+ {
+ wall_expiry /= 60;
+ s = "minute";
+ }
+
+ snprintf(buf, sizeof(buf), "expires in %d %s%s", wall_expiry, s, wall_expiry == 1 ? "" : "s");
+ }
+
+ ircdproto->SendGlobops(findbot(Config.s_OperServ), "%s added an SNLINE for %s (%s)", u->nick.c_str(), mask.c_str(), buf);
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ }
+ else
+ this->OnSyntaxError(u, "ADD");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn OnDel(User *u, const std::vector<ci::string> &params)
+ {
+ if (SNLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SNLineDelCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ XLine *x = SNLine->HasEntry(mask);
+
+ if (!x)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_NOT_FOUND, mask.c_str());
+ return MOD_CONT;
+ }
+
+ FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, X_SNLINE));
+
+ SNLineDelCallback::DoDel(u, x);
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_DELETED, mask.c_str());
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn OnList(User *u, const std::vector<ci::string> &params)
+ {
+ if (SNLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SNLineListCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SNLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SNLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_LIST_HEADER);
+ }
+
+ SNLineListCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_NO_MATCH);
+ else
+ notice_lang(Config.s_OperServ, u, END_OF_ANY_LIST, "SNLine");
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn OnView(User *u, const std::vector<ci::string> &params)
+ {
+ if (SNLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SNLineViewCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SNLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SNLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_VIEW_HEADER);
+ }
+
+ SNLineViewCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_NO_MATCH);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn OnClear(User *u)
+ {
+ FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, NULL, X_SNLINE));
+ SNLine->Clear();
+ notice_lang(Config.s_OperServ, u, OPER_SNLINE_CLEAR);
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSSNLine() : Command("SNLINE", 1, 3, "operserv/snline")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (cmd == "ADD")
+ return this->OnAdd(u, params);
+ else if (cmd == "DEL")
+ return this->OnDel(u, params);
+ else if (cmd == "LIST")
+ return this->OnList(u, params);
+ else if (cmd == "VIEW")
+ return this->OnView(u, params);
+ else if (cmd == "CLEAR")
+ return this->OnClear(u);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_SNLINE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "SNLINE", OPER_SNLINE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_SNLINE);
+ }
+};
+
+class OSSNLine : public Module
+{
+ public:
+ OSSNLine(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!ircd->snline)
+ throw ModuleException("Your IRCd does not support SNLine");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSSNLine());
+ }
+};
+
+MODULE_INIT(OSSNLine)
diff --git a/modules/core/os_sqline.cpp b/modules/core/os_sqline.cpp
new file mode 100644
index 000000000..748625d26
--- /dev/null
+++ b/modules/core/os_sqline.cpp
@@ -0,0 +1,412 @@
+/* OperServ 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 SQLineDelCallback : public NumberList
+{
+ User *u;
+ unsigned Deleted;
+ public:
+ SQLineDelCallback(User *_u, const std::string &numlist) : NumberList(numlist, true), u(_u), Deleted(0)
+ {
+ }
+
+ ~SQLineDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_NO_MATCH);
+ else if (Deleted == 1)
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_DELETED_ONE);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_DELETED_SEVERAL, Deleted);
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SQLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ ++Deleted;
+ DoDel(u, x - 1);
+ }
+
+ static void DoDel(User *u, XLine *x)
+ {
+ SQLine->DelXLine(x);
+ }
+};
+
+class SQLineListCallback : public NumberList
+{
+ protected:
+ User *u;
+ bool SentHeader;
+ public:
+ SQLineListCallback(User *_u, const std::string &numlist) : NumberList(numlist, false), u(_u), SentHeader(false)
+ {
+ }
+
+ ~SQLineListCallback()
+ {
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_NO_MATCH);
+ }
+
+ virtual void HandleNumber(unsigned Number)
+ {
+ XLine *x = SQLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_LIST_HEADER);
+ }
+
+ DoList(u, x, Number - 1);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str());
+ }
+};
+
+class SQLineViewCallback : public SQLineListCallback
+{
+ public:
+ SQLineViewCallback(User *_u, const std::string &numlist) : SQLineListCallback(_u, numlist)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SQLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_VIEW_HEADER);
+ }
+
+ DoList(u, x, Number);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ char timebuf[32], expirebuf[256];
+ struct tm tm;
+
+ tm = *localtime(&x->Created);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, &tm);
+ expire_left(u->Account(), expirebuf, sizeof(expirebuf), x->Expires);
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), timebuf, expirebuf, x->Reason.c_str());
+ }
+};
+
+
+class CommandOSSQLine : public Command
+{
+ private:
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params)
+ {
+ unsigned last_param = 2;
+ const char *expiry, *mask;
+ char reason[BUFSIZE];
+ time_t expires;
+
+ mask = params.size() > 1 ? params[1].c_str() : NULL;
+ if (mask && *mask == '+')
+ {
+ expiry = mask;
+ mask = params.size() > 2 ? params[2].c_str() : NULL;
+ last_param = 3;
+ }
+ else
+ expiry = NULL;
+
+ expires = expiry ? dotime(expiry) : Config.SQLineExpiry;
+ /* If the expiry given does not contain a final letter, it's in days,
+ * said the doc. Ah well.
+ */
+ if (expiry && isdigit(expiry[strlen(expiry) - 1]))
+ expires *= 86400;
+ /* Do not allow less than a minute expiry time */
+ if (expires != 0 && expires < 60)
+ {
+ notice_lang(Config.s_OperServ, u, BAD_EXPIRY_TIME);
+ return MOD_CONT;
+ }
+ else if (expires > 0)
+ expires += time(NULL);
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+ snprintf(reason, sizeof(reason), "%s%s%s", params[last_param].c_str(), last_param == 2 && params.size() > 3 ? " " : "", last_param == 2 && params.size() > 3 ? params[3].c_str() : "");
+ if (mask && *reason)
+ {
+ XLine *x = SQLine->Add(OperServ, u, mask, expires, reason);
+
+ if (!x)
+ return MOD_CONT;
+
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_ADDED, mask);
+
+ if (Config.WallOSSQLine)
+ {
+ char buf[128];
+
+ if (!expires)
+ strcpy(buf, "does not expire");
+ else
+ {
+ int wall_expiry = expires - time(NULL);
+ const char *s = NULL;
+
+ if (wall_expiry >= 86400)
+ {
+ wall_expiry /= 86400;
+ s = "day";
+ }
+ else if (wall_expiry >= 3600)
+ {
+ wall_expiry /= 3600;
+ s = "hour";
+ }
+ else if (wall_expiry >= 60)
+ {
+ wall_expiry /= 60;
+ s = "minute";
+ }
+
+ snprintf(buf, sizeof(buf), "expires in %d %s%s", wall_expiry, s, wall_expiry == 1 ? "" : "s");
+ }
+
+ ircdproto->SendGlobops(OperServ, "%s added an SQLINE for %s (%s)", u->nick.c_str(), mask, buf);
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ }
+ else
+ this->OnSyntaxError(u, "ADD");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params)
+ {
+ if (SQLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SQLineDelCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ XLine *x = SQLine->HasEntry(mask);
+
+ if (!x)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_NOT_FOUND, mask.c_str());
+ return MOD_CONT;
+ }
+
+ FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, X_SQLINE));
+
+ SQLineDelCallback::DoDel(u, x);
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_DELETED, mask.c_str());
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, const std::vector<ci::string> &params)
+ {
+ if (SQLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SQLineListCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SQLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SQLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_LIST_HEADER);
+ }
+
+ SQLineListCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_NO_MATCH);
+ else
+ notice_lang(Config.s_OperServ, u, END_OF_ANY_LIST, "SQLine");
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoView(User *u, const std::vector<ci::string> &params)
+ {
+ if (SQLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SQLineViewCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SQLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SQLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_VIEW_HEADER);
+ }
+
+ SQLineViewCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_NO_MATCH);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoClear(User *u)
+ {
+ FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, NULL, X_SQLINE));
+ SGLine->Clear();
+ notice_lang(Config.s_OperServ, u, OPER_SQLINE_CLEAR);
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSSQLine() : Command("SQLINE", 1, 4, "operserv/sqline")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (cmd == "ADD")
+ return this->DoAdd(u, params);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params);
+ else if (cmd == "LIST")
+ return this->DoList(u, params);
+ else if (cmd == "VIEW")
+ return this->DoView(u, params);
+ else if (cmd == "CLEAR")
+ return this->DoClear(u);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_SQLINE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "SQLINE", OPER_SQLINE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_SQLINE);
+ }
+};
+
+class OSSQLine : public Module
+{
+ public:
+ OSSQLine(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!ircd->sqline)
+ throw ModuleException("Your IRCd does not support QLines.");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSSQLine());
+ }
+};
+
+MODULE_INIT(OSSQLine)
diff --git a/modules/core/os_staff.cpp b/modules/core/os_staff.cpp
new file mode 100644
index 000000000..e36aadff8
--- /dev/null
+++ b/modules/core/os_staff.cpp
@@ -0,0 +1,82 @@
+/* OperServ 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 CommandOSStaff : public Command
+{
+ public:
+ CommandOSStaff() : Command("STAFF", 0, 0, "operserv/staff")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_STAFF_LIST_HEADER);
+
+ for (std::list<std::pair<ci::string, ci::string> >::iterator it = Config.Opers.begin(), it_end = Config.Opers.end(); it != it_end; ++it)
+ {
+ int found = 0;
+ ci::string nick = it->first, type = it->second;
+
+ NickAlias *na = findnick(nick);
+ if (na)
+ {
+ /* We have to loop all users as some may be logged into an account but not a nick */
+ for (user_map::iterator uit = UserListByNick.begin(), uit_end = UserListByNick.end(); uit != uit_end; ++uit)
+ {
+ User *u2 = uit->second;
+
+ if (u2->Account() && u2->Account() == na->nc)
+ {
+ found = 1;
+ if (na->nick == u2->nick)
+ notice_lang(Config.s_OperServ, u, OPER_STAFF_FORMAT, '*', type.c_str(), u2->nick.c_str());
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STAFF_AFORMAT, '*', type.c_str(), na->nick, u2->nick.c_str());
+ }
+ }
+ if (!found)
+ notice_lang(Config.s_OperServ, u, OPER_STAFF_FORMAT, ' ', type.c_str(), na->nick);
+ }
+ }
+
+ notice_lang(Config.s_OperServ, u, END_OF_ANY_LIST, "Staff");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_STAFF);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_STAFF);
+ }
+};
+
+class OSStaff : public Module
+{
+ public:
+ OSStaff(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSStaff());
+ }
+};
+
+MODULE_INIT(OSStaff)
diff --git a/modules/core/os_stats.cpp b/modules/core/os_stats.cpp
new file mode 100644
index 000000000..f660ffa15
--- /dev/null
+++ b/modules/core/os_stats.cpp
@@ -0,0 +1,392 @@
+/* OperServ 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"
+
+void get_operserv_stats(long *nrec, long *memuse);
+
+/**
+ * Count servers connected to server s
+ * @param s The server to start counting from
+ * @return Amount of servers connected to server s
+ **/
+static int stats_count_servers(Server *s)
+{
+ if (!s)
+ return 0;
+
+ int count = 1;
+
+ if (s->GetLinks())
+ for (std::list<Server *>::const_iterator it = s->GetLinks()->begin(), it_end = s->GetLinks()->end(); it != it_end; ++it)
+ count += stats_count_servers(*it);
+
+ return count;
+}
+
+class CommandOSStats : public Command
+{
+ private:
+ CommandReturn DoStatsAkill(User *u)
+ {
+ int timeout;
+ /* AKILLs */
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_COUNT, SGLine->GetCount());
+ timeout = Config.AutokillExpiry + 59;
+ if (timeout >= 172800)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_EXPIRE_DAYS, timeout / 86400);
+ else if (timeout >= 86400)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_EXPIRE_DAY);
+ else if (timeout >= 7200)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_EXPIRE_HOURS, timeout / 3600);
+ else if (timeout >= 3600)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_EXPIRE_HOUR);
+ else if (timeout >= 120)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_EXPIRE_MINS, timeout / 60);
+ else if (timeout >= 60)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_EXPIRE_MIN);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_AKILL_EXPIRE_NONE);
+ if (ircd->snline)
+ {
+ /* SNLINEs */
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_COUNT, SNLine->GetCount());
+ timeout = Config.SNLineExpiry + 59;
+ if (timeout >= 172800)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_EXPIRE_DAYS, timeout / 86400);
+ else if (timeout >= 86400)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_EXPIRE_DAY);
+ else if (timeout >= 7200)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_EXPIRE_HOURS, timeout / 3600);
+ else if (timeout >= 3600)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_EXPIRE_HOUR);
+ else if (timeout >= 120)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_EXPIRE_MINS, timeout / 60);
+ else if (timeout >= 60)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_EXPIRE_MIN);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SNLINE_EXPIRE_NONE);
+ }
+ if (ircd->sqline)
+ {
+ /* SQLINEs */
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_COUNT, SQLine->GetCount());
+ timeout = Config.SQLineExpiry + 59;
+ if (timeout >= 172800)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_DAYS, timeout / 86400);
+ else if (timeout >= 86400)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_DAY);
+ else if (timeout >= 7200)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_HOURS, timeout / 3600);
+ else if (timeout >= 3600)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_HOUR);
+ else if (timeout >= 120)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_MINS, timeout / 60);
+ else if (timeout >= 60)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_MIN);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SQLINE_EXPIRE_NONE);
+ }
+ if (ircd->szline)
+ {
+ /* SZLINEs */
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_COUNT, SZLine->GetCount());
+ timeout = Config.SZLineExpiry + 59;
+ if (timeout >= 172800)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_DAYS, timeout / 86400);
+ else if (timeout >= 86400)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_DAY);
+ else if (timeout >= 7200)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_HOURS, timeout / 3600);
+ else if (timeout >= 3600)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_HOUR);
+ else if (timeout >= 120)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_MINS, timeout / 60);
+ else if (timeout >= 60)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_MIN);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SZLINE_EXPIRE_NONE);
+ }
+ return MOD_CONT;
+ }
+
+ CommandReturn DoStatsReset(User *u)
+ {
+ maxusercnt = usercnt;
+ notice_lang(Config.s_OperServ, u, OPER_STATS_RESET);
+ return MOD_CONT;
+ }
+
+ CommandReturn DoStatsUptime(User *u)
+ {
+ char timebuf[64];
+ time_t uptime = time(NULL) - start_time;
+ int days = uptime / 86400, hours = (uptime / 3600) % 24, mins = (uptime / 60) % 60, secs = uptime % 60;
+ notice_lang(Config.s_OperServ, u, OPER_STATS_CURRENT_USERS, usercnt, opcnt);
+ struct tm *tm = localtime(&maxusertime);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_MAX_USERS, maxusercnt, timebuf);
+ if (days > 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_DHMS, days, hours, mins, secs);
+ else if (days == 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_1DHMS, days, hours, mins, secs);
+ else
+ {
+ if (hours > 1)
+ {
+ if (mins != 1)
+ {
+ if (secs != 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_HMS, hours, mins, secs);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_HM1S, hours, mins, secs);
+ }
+ else
+ {
+ if (secs != 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_H1MS, hours, mins, secs);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_H1M1S, hours, mins, secs);
+ }
+ }
+ else if (hours == 1)
+ {
+ if (mins != 1)
+ {
+ if (secs != 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_1HMS, hours, mins, secs);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_1HM1S, hours, mins, secs);
+ }
+ else
+ {
+ if (secs != 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_1H1MS, hours, mins, secs);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_1H1M1S, hours, mins, secs);
+ }
+ }
+ else
+ {
+ if (mins != 1)
+ {
+ if (secs != 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_MS, mins, secs);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_M1S, mins, secs);
+ }
+ else
+ {
+ if (secs != 1)
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_1MS, mins, secs);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPTIME_1M1S, mins, secs);
+ }
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoStatsUplink(User *u)
+ {
+ std::string buf;
+
+ for (unsigned j = 0; !Capab_Info[j].Token.empty(); ++j)
+ if (Capab.HasFlag(Capab_Info[j].Flag))
+ buf += " " + Capab_Info[j].Token;
+
+ if (!buf.empty())
+ buf.erase(buf.begin());
+
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPLINK_SERVER, Me->GetUplink()->GetName().c_str());
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPLINK_CAPAB, buf.c_str());
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UPLINK_SERVER_COUNT, stats_count_servers(Me->GetUplink()));
+ return MOD_CONT;
+ }
+
+ CommandReturn DoStatsMemory(User *u)
+ {
+ long count, mem;
+
+ notice_lang(Config.s_OperServ, u, OPER_STATS_BYTES_READ, TotalRead / 1024);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_BYTES_WRITTEN, TotalWritten / 1024);
+
+ get_user_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_USER_MEM, count, (mem + 512) / 1024);
+ get_channel_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_CHANNEL_MEM, count, (mem + 512) / 1024);
+ get_core_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_GROUPS_MEM, count, (mem + 512) / 1024);
+ get_aliases_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_ALIASES_MEM, count, (mem + 512) / 1024);
+ get_chanserv_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_CHANSERV_MEM, count, (mem + 512) / 1024);
+ if (Config.s_BotServ)
+ {
+ get_botserv_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_BOTSERV_MEM, count, (mem + 512) / 1024);
+ }
+ if (Config.s_HostServ)
+ {
+ get_hostserv_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_HOSTSERV_MEM, count, (mem + 512) / 1024);
+ }
+ get_operserv_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_OPERSERV_MEM, count, (mem + 512) / 1024);
+ get_session_stats(&count, &mem);
+ notice_lang(Config.s_OperServ, u, OPER_STATS_SESSIONS_MEM, count, (mem + 512) / 1024);
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSStats() : Command("STATS", 0, 1, "operserv/stats")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string extra = params.size() ? params[0] : "";
+
+ if (!extra.empty() && extra != "ALL")
+ {
+ if (extra == "AKILL")
+ return this->DoStatsAkill(u);
+ else if (extra == "RESET")
+ return this->DoStatsReset(u);
+ else if (extra != "MEMORY" && extra != "UPLINK")
+ notice_lang(Config.s_OperServ, u, OPER_STATS_UNKNOWN_OPTION, extra.c_str());
+ }
+
+ if (extra.empty() || (extra != "MEMORY" && extra != "UPLINK"))
+ this->DoStatsUptime(u);
+
+ if (!extra.empty() && (extra == "ALL" || extra == "UPLINK"))
+ this->DoStatsUplink(u);
+
+ if (!extra.empty() && (extra == "ALL" || extra == "MEMORY"))
+ this->DoStatsMemory(u);
+
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_STATS);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_STATS);
+ }
+};
+
+class OSStats : public Module
+{
+ public:
+ OSStats(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSStats());
+ }
+};
+
+void get_operserv_stats(long *nrec, long *memuse)
+{
+ unsigned i, end;
+ long mem = 0, count = 0, mem2 = 0, count2 = 0;
+ XLine *x;
+
+ end = SGLine->GetCount();
+ count += end;
+ mem += end * sizeof(XLine);
+
+ for (i = 0; i < end; ++i)
+ {
+ x = SGLine->GetEntry(i);
+
+ mem += x->GetNick().length() + 1;
+ mem += x->GetUser().length() + 1;
+ mem += x->GetHost().length() + 1;
+ mem += x->Mask.length() + 1;
+ mem += x->By.length() + 1;
+ mem += x->Reason.length() + 1;
+ }
+
+ if (ircd->snline)
+ {
+ end = SNLine->GetCount();
+ count += end;
+ mem += end * sizeof(XLine);
+
+ for (i = 0; i < end; ++i)
+ {
+ x = SNLine->GetEntry(i);
+
+ mem += x->GetNick().length() + 1;
+ mem += x->GetUser().length() + 1;
+ mem += x->GetHost().length() + 1;
+ mem += x->Mask.length() + 1;
+ mem += x->By.length() + 1;
+ mem += x->Reason.length() + 1;
+ }
+ }
+ if (ircd->sqline)
+ {
+ end = SQLine->GetCount();
+ count += end;
+ mem += end * sizeof(XLine);
+
+ for (i = 0; i < end; ++i)
+ {
+ x = SNLine->GetEntry(i);
+
+ mem += x->GetNick().length() + 1;
+ mem += x->GetUser().length() + 1;
+ mem += x->GetHost().length() + 1;
+ mem += x->Mask.length() + 1;
+ mem += x->By.length() + 1;
+ mem += x->Reason.length() + 1;
+ }
+ }
+ if (ircd->szline)
+ {
+ end = SZLine->GetCount();
+ count += end;
+ mem += end * sizeof(XLine);
+
+ for (i = 0; i < end; ++i)
+ {
+ x = SZLine->GetEntry(i);
+
+ mem += x->GetNick().length() + 1;
+ mem += x->GetUser().length() + 1;
+ mem += x->GetHost().length() + 1;
+ mem += x->Mask.length() + 1;
+ mem += x->By.length() + 1;
+ mem += x->Reason.length() + 1;
+ }
+ }
+
+ get_exception_stats(&count2, &mem2);
+ count += count2;
+ mem += mem2;
+
+ *nrec = count;
+ *memuse = mem;
+}
+
+MODULE_INIT(OSStats)
diff --git a/modules/core/os_svsnick.cpp b/modules/core/os_svsnick.cpp
new file mode 100644
index 000000000..a7a8e7a2c
--- /dev/null
+++ b/modules/core/os_svsnick.cpp
@@ -0,0 +1,99 @@
+/* OperServ 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 CommandOSSVSNick : public Command
+{
+ public:
+ CommandOSSVSNick() : Command("SVSNICK", 2, 2, "operserv/svsnick")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ ci::string newnick = params[1];
+ User *u2;
+
+ NickAlias *na;
+
+ /* Truncate long nicknames to NICKMAX-2 characters */
+ if (newnick.length() > Config.NickLen)
+ {
+ notice_lang(Config.s_OperServ, u, NICK_X_TRUNCATED, newnick.c_str(), Config.NickLen, newnick.c_str());
+ newnick = params[1].substr(0, Config.NickLen);
+ }
+
+ /* Check for valid characters */
+ if (newnick[0] == '-' || isdigit(newnick[0]))
+ {
+ notice_lang(Config.s_OperServ, u, NICK_X_ILLEGAL, newnick.c_str());
+ return MOD_CONT;
+ }
+ for (unsigned i = 0, end = newnick.size(); i < end; ++i)
+ if (!isvalidnick(newnick[i]))
+ {
+ notice_lang(Config.s_OperServ, u, NICK_X_ILLEGAL, newnick.c_str());
+ return MOD_CONT;
+ }
+
+ /* Check for a nick in use or a forbidden/suspended nick */
+ if (!(u2 = finduser(nick)))
+ notice_lang(Config.s_OperServ, u, NICK_X_NOT_IN_USE, nick);
+ else if (finduser(newnick))
+ notice_lang(Config.s_OperServ, u, NICK_X_IN_USE, newnick.c_str());
+ else if ((na = findnick(newnick)) && (na->HasFlag(NS_FORBIDDEN)))
+ notice_lang(Config.s_OperServ, u, NICK_X_FORBIDDEN, newnick.c_str());
+ else
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SVSNICK_NEWNICK, nick, newnick.c_str());
+ ircdproto->SendGlobops(OperServ, "%s used SVSNICK to change %s to %s", u->nick.c_str(), nick, newnick.c_str());
+ ircdproto->SendForceNickChange(u2, newnick.c_str(), time(NULL));
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_SVSNICK);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "SVSNICK", OPER_SVSNICK_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_SVSNICK);
+ }
+};
+
+class OSSVSNick : public Module
+{
+ public:
+ OSSVSNick(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!ircd->svsnick)
+ throw ModuleException("Your IRCd does not support SVSNICK");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSSVSNick());
+ }
+};
+
+MODULE_INIT(OSSVSNick)
diff --git a/modules/core/os_szline.cpp b/modules/core/os_szline.cpp
new file mode 100644
index 000000000..3b1a09009
--- /dev/null
+++ b/modules/core/os_szline.cpp
@@ -0,0 +1,409 @@
+/* OperServ 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 SZLineDelCallback : public NumberList
+{
+ User *u;
+ unsigned Deleted;
+ public:
+ SZLineDelCallback(User *_u, const std::string &numlist) : NumberList(numlist, true), u(_u), Deleted(0)
+ {
+ }
+
+ ~SZLineDelCallback()
+ {
+ if (!Deleted)
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_NO_MATCH);
+ else if (Deleted == 0)
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_DELETED_ONE);
+ else
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_DELETED_SEVERAL, Deleted);
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SZLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ ++Deleted;
+ DoDel(u, x);
+ }
+
+ static void DoDel(User *u, XLine *x)
+ {
+ SZLine->DelXLine(x);
+ }
+};
+
+class SZLineListCallback : public NumberList
+{
+ protected:
+ User *u;
+ bool SentHeader;
+ public:
+ SZLineListCallback(User *_u, const std::string &numlist) : NumberList(numlist, false), u(_u), SentHeader(false)
+ {
+ }
+
+ ~SZLineListCallback()
+ {
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_NO_MATCH);
+ }
+
+ virtual void HandleNumber(unsigned Number)
+ {
+ XLine *x = SZLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_LIST_HEADER);
+ }
+
+ DoList(u, x, Number - 1);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_LIST_FORMAT, Number + 1, x->Mask.c_str(), x->Reason.c_str());
+ }
+};
+
+class SZLineViewCallback : public SZLineListCallback
+{
+ public:
+ SZLineViewCallback(User *_u, const std::string &numlist) : SZLineListCallback(_u, numlist)
+ {
+ }
+
+ void HandleNumber(unsigned Number)
+ {
+ XLine *x = SZLine->GetEntry(Number - 1);
+
+ if (!x)
+ return;
+
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_VIEW_HEADER);
+ }
+
+ DoList(u, x, Number - 1);
+ }
+
+ static void DoList(User *u, XLine *x, unsigned Number)
+ {
+ char timebuf[32], expirebuf[256];
+ struct tm tm;
+
+ tm = *localtime(&x->Created);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, &tm);
+ expire_left(u->Account(), expirebuf, sizeof(expirebuf), x->Expires);
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_VIEW_FORMAT, Number + 1, x->Mask.c_str(), x->By.c_str(), timebuf, expirebuf, x->Reason.c_str());
+ }
+};
+
+class CommandOSSZLine : public Command
+{
+ private:
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params)
+ {
+ unsigned last_param = 2;
+ const char *expiry, *mask;
+ char reason[BUFSIZE];
+ time_t expires;
+
+ mask = params.size() > 1 ? params[1].c_str() : NULL;
+ if (mask && *mask == '+')
+ {
+ expiry = mask;
+ mask = params.size() > 2 ? params[2].c_str() : NULL;
+ last_param = 3;
+ }
+ else
+ expiry = NULL;
+
+ expires = expiry ? dotime(expiry) : Config.SZLineExpiry;
+ /* If the expiry given does not contain a final letter, it's in days,
+ * said the doc. Ah well.
+ */
+ if (expiry && isdigit(expiry[strlen(expiry) - 1]))
+ expires *= 86400;
+ /* Do not allow less than a minute expiry time */
+ if (expires != 0 && expires < 60)
+ {
+ notice_lang(Config.s_OperServ, u, BAD_EXPIRY_TIME);
+ return MOD_CONT;
+ }
+ else if (expires > 0)
+ expires += time(NULL);
+
+ if (params.size() <= last_param)
+ {
+ this->OnSyntaxError(u, "ADD");
+ return MOD_CONT;
+ }
+ snprintf(reason, sizeof(reason), "%s%s%s", params[last_param].c_str(), last_param == 2 && params.size() > 3 ? " " : "", last_param == 2 && params.size() > 3 ? params[3].c_str() : "");
+ if (mask && *reason)
+ {
+ XLine *x = SZLine->Add(OperServ, u, mask, expires, reason);
+
+ if (!x)
+ return MOD_CONT;
+
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_ADDED, mask);
+
+ if (Config.WallOSSZLine)
+ {
+ char buf[128];
+
+ if (!expires)
+ strcpy(buf, "does not expire");
+ else
+ {
+ int wall_expiry = expires - time(NULL);
+ const char *s = NULL;
+
+ if (wall_expiry >= 86400)
+ {
+ wall_expiry /= 86400;
+ s = "day";
+ }
+ else if (wall_expiry >= 3600)
+ {
+ wall_expiry /= 3600;
+ s = "hour";
+ }
+ else if (wall_expiry >= 60)
+ {
+ wall_expiry /= 60;
+ s = "minute";
+ }
+
+ snprintf(buf, sizeof(buf), "expires in %d %s%s", wall_expiry, s, wall_expiry == 1 ? "" : "s");
+ }
+
+ ircdproto->SendGlobops(OperServ, "%s added an SZLINE for %s (%s)", u->nick.c_str(), mask, buf);
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ }
+ else
+ this->OnSyntaxError(u, "ADD");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params)
+ {
+ if (SZLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1].c_str() : "";
+
+ if (mask.empty())
+ {
+ this->OnSyntaxError(u, "DEL");
+ return MOD_CONT;
+ }
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SZLineDelCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ XLine *x = SZLine->HasEntry(mask);
+
+ if (!x)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_NOT_FOUND, mask.c_str());
+ return MOD_CONT;
+ }
+
+ FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, x, X_SZLINE));
+
+ SZLineDelCallback::DoDel(u, x);
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_DELETED, mask.c_str());
+ }
+
+ if (readonly)
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoList(User *u, const std::vector<ci::string> &params)
+ {
+ if (SZLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SZLineListCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SZLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SZLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_LIST_HEADER);
+ }
+
+ SZLineListCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_NO_MATCH);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoView(User *u, const std::vector<ci::string> &params)
+ {
+ if (SZLine->GetList().empty())
+ {
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_LIST_EMPTY);
+ return MOD_CONT;
+ }
+
+ const ci::string mask = params.size() > 1 ? params[1] : "";
+
+ if (!mask.empty() && isdigit(mask[0]) && strspn(mask.c_str(), "1234567890,-") == mask.length())
+ {
+ SZLineViewCallback list(u, mask.c_str());
+ list.Process();
+ }
+ else
+ {
+ bool SentHeader = false;
+
+ for (unsigned i = 0, end = SZLine->GetCount(); i < end; ++i)
+ {
+ XLine *x = SZLine->GetEntry(i);
+
+ if (mask.empty() || mask == x->Mask || Anope::Match(x->Mask, mask))
+ {
+ if (!SentHeader)
+ {
+ SentHeader = true;
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_VIEW_HEADER);
+ }
+
+ SZLineViewCallback::DoList(u, x, i);
+ }
+ }
+
+ if (!SentHeader)
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_NO_MATCH);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoClear(User *u)
+ {
+ FOREACH_MOD(I_OnDelXLine, OnDelXLine(u, NULL, X_SZLINE));
+ SZLine->Clear();
+ notice_lang(Config.s_OperServ, u, OPER_SZLINE_CLEAR);
+
+ return MOD_CONT;
+ }
+ public:
+ CommandOSSZLine() : Command("SZLINE", 1, 4, "operserv/szline")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ci::string cmd = params[0];
+
+ if (cmd == "ADD")
+ return this->DoAdd(u, params);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params);
+ else if (cmd == "LIST")
+ return this->DoList(u, params);
+ else if (cmd == "VIEW")
+ return this->DoView(u, params);
+ else if (cmd == "CLEAR")
+ return this->DoClear(u);
+ else
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_SZLINE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "SZLINE", OPER_SZLINE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_SZLINE);
+ }
+};
+
+class OSSZLine : public Module
+{
+ public:
+ OSSZLine(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!ircd->szline)
+ throw ModuleException("Your IRCd does not support ZLINEs");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSSZLine());
+ }
+};
+
+MODULE_INIT(OSSZLine)
diff --git a/modules/core/os_umode.cpp b/modules/core/os_umode.cpp
new file mode 100644
index 000000000..cb353a938
--- /dev/null
+++ b/modules/core/os_umode.cpp
@@ -0,0 +1,86 @@
+/* OperServ 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 CommandOSUMode : public Command
+{
+ public:
+ CommandOSUMode() : Command("UMODE", 2, 2, "operserv/umode")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *nick = params[0].c_str();
+ const char *modes = params[1].c_str();
+
+ User *u2;
+
+ /**
+ * Only accept a +/- mode string
+ *-rob
+ **/
+ if (modes[0] != '+' && modes[0] != '-')
+ {
+ this->OnSyntaxError(u, "");
+ return MOD_CONT;
+ }
+ if (!(u2 = finduser(nick)))
+ notice_lang(Config.s_OperServ, u, NICK_X_NOT_IN_USE, nick);
+ else
+ {
+ u2->SetModes(OperServ, modes);
+
+ notice_lang(Config.s_OperServ, u, OPER_UMODE_SUCCESS, nick);
+ notice_lang(Config.s_OperServ, u2, OPER_UMODE_CHANGED, u->nick.c_str());
+
+ if (Config.WallOSMode)
+ ircdproto->SendGlobops(OperServ, "\2%s\2 used UMODE on %s", u->nick.c_str(), nick);
+ }
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_UMODE);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "UMODE", OPER_UMODE_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_UMODE);
+ }
+};
+
+class OSUMode : public Module
+{
+ public:
+ OSUMode(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ if (!ircd->umode)
+ throw ModuleException("Your IRCd does not support setting umodes");
+
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSUMode());
+ }
+};
+
+MODULE_INIT(OSUMode)
diff --git a/modules/core/os_update.cpp b/modules/core/os_update.cpp
new file mode 100644
index 000000000..68cffd4ba
--- /dev/null
+++ b/modules/core/os_update.cpp
@@ -0,0 +1,54 @@
+/* OperServ 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 CommandOSUpdate : public Command
+{
+ public:
+ CommandOSUpdate() : Command("UPDATE", 0, 0, "operserv/update")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_UPDATING);
+ save_data = 1;
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_UPDATE);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_UPDATE);
+ }
+};
+
+class OSUpdate : public Module
+{
+ public:
+ OSUpdate(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSUpdate());
+ }
+};
+
+MODULE_INIT(OSUpdate)
diff --git a/modules/core/os_userlist.cpp b/modules/core/os_userlist.cpp
new file mode 100644
index 000000000..9005016d5
--- /dev/null
+++ b/modules/core/os_userlist.cpp
@@ -0,0 +1,100 @@
+/* OperServ 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 CommandOSUserList : public Command
+{
+ public:
+ CommandOSUserList() : Command("USERLIST", 0, 2)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ const char *pattern = params.size() > 0 ? params[0].c_str() : NULL;
+ ci::string opt = params.size() > 1 ? params[1] : "";
+ Channel *c;
+ std::list<UserModeName> Modes;
+
+ if (!opt.empty() && opt == "INVISIBLE")
+ Modes.push_back(UMODE_INVIS);
+
+ if (pattern && (c = findchan(pattern)))
+ {
+ notice_lang(Config.s_OperServ, u, OPER_USERLIST_HEADER_CHAN, pattern);
+
+ for (CUserList::iterator cuit = c->users.begin(), cuit_end = c->users.end(); cuit != cuit_end; ++cuit)
+ {
+ UserContainer *uc = *cuit;
+
+ if (!Modes.empty())
+ for (std::list<UserModeName>::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it)
+ if (!uc->user->HasMode(*it))
+ continue;
+
+ notice_lang(Config.s_OperServ, u, OPER_USERLIST_RECORD, uc->user->nick.c_str(), uc->user->GetIdent().c_str(), uc->user->GetDisplayedHost().c_str());
+ }
+ }
+ else
+ {
+ notice_lang(Config.s_OperServ, u, OPER_USERLIST_HEADER);
+
+ for (user_map::const_iterator uit = UserListByNick.begin(), uit_end = UserListByNick.end(); uit != uit_end; ++uit)
+ {
+ User *u2 = uit->second;
+
+ if (pattern)
+ {
+ char mask[BUFSIZE];
+ snprintf(mask, sizeof(mask), "%s!%s@%s", u2->nick.c_str(), u2->GetIdent().c_str(), u2->GetDisplayedHost().c_str());
+ if (!Anope::Match(mask, pattern, false))
+ continue;
+ if (!Modes.empty())
+ for (std::list<UserModeName>::iterator it = Modes.begin(), it_end = Modes.end(); it != it_end; ++it)
+ if (!u2->HasMode(*it))
+ continue;
+ }
+ notice_lang(Config.s_OperServ, u, OPER_USERLIST_RECORD, u2->nick.c_str(), u2->GetIdent().c_str(), u2->GetDisplayedHost().c_str());
+ }
+ }
+
+ notice_lang(Config.s_OperServ, u, OPER_USERLIST_END);
+ return MOD_CONT;
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, OPER_HELP_USERLIST);
+ return true;
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_USERLIST);
+ }
+};
+
+class OSUserList : public Module
+{
+ public:
+ OSUserList(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSUserList());
+ }
+};
+
+MODULE_INIT(OSUserList)
diff --git a/modules/core/ss_main.cpp b/modules/core/ss_main.cpp
new file mode 100644
index 000000000..a48b77855
--- /dev/null
+++ b/modules/core/ss_main.cpp
@@ -0,0 +1,63 @@
+/* StatServ 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"
+
+BotInfo *statserv = NULL;
+
+class CommandSSHelp : public Command
+{
+ public:
+ CommandSSHelp() : Command("HELP", 0, 0)
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ ircdproto->SendMessage(statserv, u->nick.c_str(), "This is a test of the emergency StatServ system.");
+ return MOD_CONT;
+ }
+};
+
+class SSMain : public Module
+{
+ public:
+ SSMain(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetType(CORE);
+ this->SetPermanent(true);
+
+ statserv = findbot("StatServ");
+ if (!statserv)
+ {
+ Alog() << "Creating SS";
+ statserv = new BotInfo("StatServ", Config.ServiceUser, Config.ServiceHost, "Stats Service");
+ }
+ Alog() << "Done creating SS";
+
+ this->AddCommand(statserv, new CommandSSHelp());
+ }
+
+ ~SSMain()
+ {
+ if (statserv)
+ {
+ for (std::map<ci::string, Command *>::iterator it = statserv->Commands.begin(), it_end = statserv->Commands.end(); it != it_end; ++it)
+ this->DelCommand(statserv, it->second);
+
+ ircdproto->SendQuit(statserv, "Quit due to module unload.");
+ delete statserv;
+ }
+ }
+};
+
+MODULE_INIT(SSMain)