diff options
| author | Adam <Adam@anope.org> | 2010-07-15 22:55:02 -0400 |
|---|---|---|
| committer | Adam <Adam@anope.org> | 2010-07-15 22:55:02 -0400 |
| commit | a22f8d3b2de88b9bb6f80f0c2780846ae23ab389 (patch) | |
| tree | a2fdf570868896a787df8a1169951b0028e68d6b /modules/core | |
| parent | 43b1e43afb85639485e36d24da351dc0f121be6e (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')
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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms, 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> ¶ms, 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> ¶ms, 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> ¶ms, 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> ¶ms, 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> ¶ms) = 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) +{ + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms, 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> ¶ms, 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms, 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> ¶ms) + { + 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> ¶ms) + { + /* + * 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + /* 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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 ¶m) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms, 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> ¶ms, 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> ¶ms, 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> ¶ms) = 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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> ¶ms) + { + 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) |
