diff options
| author | Adam <Adam@anope.org> | 2011-08-05 05:35:31 -0400 |
|---|---|---|
| committer | Adam <Adam@anope.org> | 2011-08-05 05:35:31 -0400 |
| commit | e66063e6304538d34c40460ca0aa2be5ddb6bdec (patch) | |
| tree | f50fe31097160f8f794669809e4f4ef87f477672 /modules/pseudoclients | |
| parent | 9ec18a3b020932eee6242c878149c484f49b13cb (diff) | |
Rewrote the example configurations and split them
up into seperate files for each pseudo client.
Also reorganized how the modules are stored, and
made most of the old "extra" modules "core"
Diffstat (limited to 'modules/pseudoclients')
| -rw-r--r-- | modules/pseudoclients/botserv.h | 63 | ||||
| -rw-r--r-- | modules/pseudoclients/bs_main.cpp | 204 | ||||
| -rw-r--r-- | modules/pseudoclients/cs_main.cpp | 143 | ||||
| -rw-r--r-- | modules/pseudoclients/gl_main.cpp | 93 | ||||
| -rw-r--r-- | modules/pseudoclients/global.h | 20 | ||||
| -rw-r--r-- | modules/pseudoclients/hs_main.cpp | 85 | ||||
| -rw-r--r-- | modules/pseudoclients/memoserv.h | 41 | ||||
| -rw-r--r-- | modules/pseudoclients/ms_main.cpp | 228 | ||||
| -rw-r--r-- | modules/pseudoclients/nickserv.h | 15 | ||||
| -rw-r--r-- | modules/pseudoclients/ns_main.cpp | 307 | ||||
| -rw-r--r-- | modules/pseudoclients/os_main.cpp | 371 |
11 files changed, 1570 insertions, 0 deletions
diff --git a/modules/pseudoclients/botserv.h b/modules/pseudoclients/botserv.h new file mode 100644 index 000000000..5fa03f870 --- /dev/null +++ b/modules/pseudoclients/botserv.h @@ -0,0 +1,63 @@ +#ifndef BOTSERV_H +#define BOTSERV_H + +struct UserData +{ + UserData() + { + this->Clear(); + } + + void Clear() + { + last_use = last_start = Anope::CurTime; + lines = times = 0; + lastline.clear(); + } + + /* Data validity */ + time_t last_use; + + /* for flood kicker */ + int16 lines; + time_t last_start; + + /* for repeat kicker */ + Anope::string lastline; + Anope::string lasttarget; + int16 times; +}; + +struct BanData +{ + Anope::string mask; + time_t last_use; + int16 ttb[TTB_SIZE]; + + BanData() + { + this->Clear(); + } + + void Clear() + { + last_use = 0; + for (int i = 0; i < TTB_SIZE; ++i) + this->ttb[i] = 0; + } +}; + +class BotServService : public Service +{ + public: + BotServService(Module *m) : Service(m, "BotServ") { } + + virtual UserData *GetUserData(User *u, Channel *c) = 0; + + virtual BanData *GetBanData(User *u, Channel *c) = 0; +}; + +static service_reference<BotServService> botserv("BotServ"); + +#endif // BOTSERV_H + diff --git a/modules/pseudoclients/bs_main.cpp b/modules/pseudoclients/bs_main.cpp new file mode 100644 index 000000000..69eab6040 --- /dev/null +++ b/modules/pseudoclients/bs_main.cpp @@ -0,0 +1,204 @@ +/* BotServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 BotServCore : public Module +{ + Channel *fantasy_channel; + public: + BotServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), fantasy_channel(NULL) + { + this->SetAuthor("Anope"); + + BotInfo *BotServ = findbot(Config->BotServ); + if (BotServ == NULL) + throw ModuleException("No bot named " + Config->BotServ); + + Implementation i[] = { I_OnPrivmsg, I_OnPreCommand, I_OnJoinChannel, I_OnLeaveChannel, + I_OnPreHelp, I_OnPostHelp, I_OnChannelModeSet }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + } + + void OnPrivmsg(User *u, Channel *c, Anope::string &msg) + { + if (!u || !c || !c->ci || !c->ci->bi || msg.empty()) + return; + + /* Answer to ping if needed */ + if (msg.substr(0, 6).equals_ci("\1PING ") && msg[msg.length() - 1] == '\1') + { + Anope::string ctcp = msg; + ctcp.erase(ctcp.begin()); + ctcp.erase(ctcp.length() - 1); + ircdproto->SendCTCP(c->ci->bi, u->nick, "%s", ctcp.c_str()); + } + + + Anope::string realbuf = msg; + + bool was_action = false; + if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1') + { + realbuf.erase(0, 8); + realbuf.erase(realbuf.length() - 1); + was_action = true; + } + + if (realbuf.empty()) + return; + + /* Fantaisist commands */ + if (c->ci->botflags.HasFlag(BS_FANTASY) && realbuf[0] == Config->BSFantasyCharacter[0] && !was_action) + { + /* Strip off the fantasy character */ + realbuf.erase(realbuf.begin()); + + size_t space = realbuf.find(' '); + Anope::string command, rest; + if (space == Anope::string::npos) + command = realbuf; + else + { + command = realbuf.substr(0, space); + rest = realbuf.substr(space + 1); + } + + BotInfo *bi = findbot(Config->ChanServ); + if (bi == NULL) + bi = findbot(Config->BotServ); + if (bi == NULL || !bi->commands.count(command)) + return; + + if (c->ci->HasPriv(u, CA_FANTASIA)) + { + + this->fantasy_channel = c; + bi->OnMessage(u, realbuf); + this->fantasy_channel = NULL; + + FOREACH_MOD(I_OnBotFantasy, OnBotFantasy(command, u, c->ci, rest)); + } + else + { + FOREACH_MOD(I_OnBotNoFantasyAccess, OnBotNoFantasyAccess(command, u, c->ci, rest)); + } + } + } + + EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> ¶ms) + { + if (this->fantasy_channel != NULL) + { + if (!command->HasFlag(CFLAG_STRIP_CHANNEL)) + params.insert(params.begin(), this->fantasy_channel->name); + source.c = this->fantasy_channel; + } + + return EVENT_CONTINUE; + } + + void OnJoinChannel(User *user, Channel *c) + { + if (c->ci && c->ci->bi) + { + /** + * We let the bot join even if it was an ignored user, as if we don't, + * and the ignored user doesnt just leave, the bot will never + * make it into the channel, leaving the channel botless even for + * legit users - Rob + **/ + if (c->users.size() >= Config->BSMinUsers && !c->FindUser(c->ci->bi)) + c->ci->bi->Join(c, &Config->BotModeList); + /* Only display the greet if the main uplink we're connected + * to has synced, or we'll get greet-floods when the net + * recovers from a netsplit. -GD + */ + if (c->FindUser(c->ci->bi) && c->ci->botflags.HasFlag(BS_GREET) && user->Account() && !user->Account()->greet.empty() && c->ci->HasPriv(user, CA_GREET) && user->server->IsSynced()) + { + ircdproto->SendPrivmsg(c->ci->bi, c->name, "[%s] %s", user->Account()->display.c_str(), user->Account()->greet.c_str()); + c->ci->bi->lastmsg = Anope::CurTime; + } + } + } + + void OnLeaveChannel(User *u, Channel *c) + { + /* Channel is persistant, it shouldn't be deleted and the service bot should stay */ + if (c->HasFlag(CH_PERSIST) || (c->ci && c->ci->HasFlag(CI_PERSIST))) + return; + + /* Channel is syncing from a netburst, don't destroy it as more users are probably wanting to join immediatly + * We also don't part the bot here either, if necessary we will part it after the sync + */ + if (c->HasFlag(CH_SYNCING)) + return; + + /* Additionally, do not delete this channel if ChanServ/a BotServ bot is inhabiting it */ + if (c->HasFlag(CH_INHABIT)) + return; + + if (c->ci && c->ci->bi && c->users.size() - 1 <= Config->BSMinUsers && c->FindUser(c->ci->bi)) + { + bool persist = c->HasFlag(CH_PERSIST); + c->SetFlag(CH_PERSIST); + c->ci->bi->Part(c->ci->c); + if (!persist) + c->UnsetFlag(CH_PERSIST); + } + } + + void OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->BotServ) + return; + source.Reply(_("\002%s\002 allows you to have a bot on your own channel.\n" + "It has been created for users that can't host or\n" + "configure a bot, or for use on networks that don't\n" + "allow user bots. Available commands are listed \n" + "below; to use them, type \002%s%s \037command\037\002. For\n" + "more information on a specific command, type\n" + "\002%s%s %s \037command\037\002.\n "), + Config->BotServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->BotServ.c_str(), + Config->UseStrictPrivMsgString.c_str(), Config->BotServ.c_str(), source.command.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->BotServ) + return; + source.Reply(_(" \n" + "Bot will join a channel whenever there is at least\n" + "\002%d\002 user(s) on it. Additionally, all %s commands\n" + "can be used if fantasy is enabled by prefixing the command\n" + "name with a %c."), Config->BSMinUsers, Config->ChanServ.c_str(), Config->BSFantasyCharacter[0]); + } + + EventReturn OnChannelModeSet(Channel *c, ChannelModeName Name, const Anope::string ¶m) + { + if (Config->BSSmartJoin && Name == CMODE_BAN && c->ci && c->ci->bi && c->FindUser(c->ci->bi)) + { + BotInfo *bi = c->ci->bi; + + Entry ban(CMODE_BAN, param); + if (ban.Matches(bi)) + c->RemoveMode(bi, CMODE_BAN, param); + } + + return EVENT_CONTINUE; + } +}; + +MODULE_INIT(BotServCore) + diff --git a/modules/pseudoclients/cs_main.cpp b/modules/pseudoclients/cs_main.cpp new file mode 100644 index 000000000..5c490e4ff --- /dev/null +++ b/modules/pseudoclients/cs_main.cpp @@ -0,0 +1,143 @@ +/* ChanServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 ChanServCore : public Module +{ + public: + ChanServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE) + { + this->SetAuthor("Anope"); + + BotInfo *ChanServ = findbot(Config->ChanServ); + if (ChanServ == NULL) + throw ModuleException("No bot named " + Config->ChanServ); + + Implementation i[] = { I_OnBotPrivmsg, I_OnDelCore, I_OnPreHelp, I_OnPostHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) + { + if (Config->CSOpersOnly && !u->HasMode(UMODE_OPER) && bi->nick == Config->ChanServ) + { + u->SendMessage(bi, ACCESS_DENIED); + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + void OnDelCore(NickCore *nc) + { + // XXX this is slightly inefficient + for (registered_channel_map::const_iterator it = RegisteredChannelList.begin(), it_end = RegisteredChannelList.end(); it != it_end;) + { + ChannelInfo *ci = it->second; + ++it; + + if (ci->GetFounder() == nc) + { + NickCore *newowner = NULL; + if (ci->successor && (ci->successor->IsServicesOper() || !Config->CSMaxReg || ci->successor->channelcount < Config->CSMaxReg)) + newowner = ci->successor; + else + { + ChanAccess *highest = NULL; + for (unsigned j = 0; j < ci->GetAccessCount(); ++j) + { + ChanAccess *ca = ci->GetAccess(j); + NickCore *anc = findcore(ca->mask); + + if (!anc || (!anc->IsServicesOper() && Config->CSMaxReg && anc->channelcount >= Config->CSMaxReg) || (anc == nc)) + continue; + if (!highest || *ca > *highest) + highest = ca; + } + if (highest) + newowner = findcore(highest->mask); + } + + if (newowner) + { + Log(LOG_NORMAL, "chanserv/expire") << "Transferring foundership of " << ci->name << " from deleted nick " << nc->display << " to " << newowner->display; + ci->SetFounder(newowner); + ci->successor = NULL; + } + else + { + Log(LOG_NORMAL, "chanserv/expire") << "Deleting channel " << ci->name << " owned by deleted nick " << nc->display; + + delete ci; + continue; + } + } + + if (ci->successor == nc) + ci->successor = NULL; + + for (unsigned j = 0; j < ci->GetAccessCount(); ++j) + { + ChanAccess *ca = ci->GetAccess(j); + NickCore *anc = findcore(ca->mask); + + if (anc && anc == nc) + { + ci->EraseAccess(j); + break; + } + } + + for (unsigned j = ci->GetAkickCount(); j > 0; --j) + { + AutoKick *akick = ci->GetAkick(j - 1); + if (akick->HasFlag(AK_ISNICK) && akick->nc == nc) + ci->EraseAkick(j - 1); + } + } + } + + void OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->ChanServ) + return; + source.Reply(_("\002%s\002 allows you to register and control various\n" + "aspects of channels. %s can often prevent\n" + "malicious users from \"taking over\" channels by limiting\n" + "who is allowed channel operator privileges. Available\n" + "commands are listed below; to use them, type\n" + "\002%s%s \037command\037\002. For more information on a\n" + "specific command, type \002%s%s HELP \037command\037\002.\n "), + Config->ChanServ.c_str(), Config->ChanServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->ChanServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->ChanServ.c_str(), Config->ChanServ.c_str(), source.command.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->ChanServ) + return; + if (Config->CSExpire >= 86400) + source.Reply(_(" \n" + "Note that any channel which is not used for %d days\n" + "(i.e. which no user on the channel's access list enters\n" + "for that period of time) will be automatically dropped."), Config->CSExpire / 86400); + if (source.u->IsServicesOper()) + source.Reply(_(" \n" + "Services Operators can also drop any channel without needing\n" + "to identify via password, and may view the access, akick,\n" + "and level setting lists for any channel.")); + } +}; + +MODULE_INIT(ChanServCore) + diff --git a/modules/pseudoclients/gl_main.cpp b/modules/pseudoclients/gl_main.cpp new file mode 100644 index 000000000..c7ccf9209 --- /dev/null +++ b/modules/pseudoclients/gl_main.cpp @@ -0,0 +1,93 @@ +/* Global core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 "global.h" + +class MyGlobalService : public GlobalService +{ + void ServerGlobal(Server *s, const Anope::string &message) + { + if (s != Me && !s->HasFlag(SERVER_JUPED)) + notice_server(Config->Global, s, "%s", message.c_str()); + for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i) + this->ServerGlobal(s->GetLinks()[i], message); + } + + public: + MyGlobalService(Module *m) : GlobalService(m) { } + + void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) + { + if (Me->GetLinks().empty()) + return; + + Anope::string rmessage; + + if (!source.empty() && !Config->AnonymousGlobal) + rmessage = "[" + source + "] " + message; + else + rmessage = message; + + this->ServerGlobal(Me->GetLinks().front(), rmessage); + } +}; + +class GlobalCore : public Module +{ + MyGlobalService myglobalservice; + + public: + GlobalCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + myglobalservice(this) + { + this->SetAuthor("Anope"); + + BotInfo *Global = findbot(Config->Global); + if (Global == NULL) + throw ModuleException("No bot named " + Config->Global); + + ModuleManager::RegisterService(&this->myglobalservice); + + Implementation i[] = { I_OnRestart, I_OnShutdown, I_OnNewServer, I_OnPreHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnRestart() + { + if (Config->GlobalOnCycle) + global->SendGlobal(findbot(Config->Global), "", Config->GlobalOnCycleMessage); + } + + void OnShutdown() + { + if (Config->GlobalOnCycle) + global->SendGlobal(findbot(Config->Global), "", Config->GlobalOnCycleMessage); + } + + void OnNewServer(Server *s) + { + if (Config->GlobalOnCycle && !Config->GlobalOnCycleUP.empty()) + notice_server(Config->Global, s, "%s", Config->GlobalOnCycleUP.c_str()); + } + + void OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->Global) + return; + source.Reply(_("%s commands:\n"), Config->Global.c_str()); + } +}; + +MODULE_INIT(GlobalCore) + diff --git a/modules/pseudoclients/global.h b/modules/pseudoclients/global.h new file mode 100644 index 000000000..454e879c2 --- /dev/null +++ b/modules/pseudoclients/global.h @@ -0,0 +1,20 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +class GlobalService : public Service +{ + public: + GlobalService(Module *m) : Service(m, "Global") { } + + /** Send out a global message to all users + * @param sender Our client which should send the global + * @param source The sender of the global + * @param message The message + */ + virtual void SendGlobal(BotInfo *sender, const Anope::string &source, const Anope::string &message) = 0; +}; + +static service_reference<GlobalService> global("Global"); + +#endif // GLOBAL_H + diff --git a/modules/pseudoclients/hs_main.cpp b/modules/pseudoclients/hs_main.cpp new file mode 100644 index 000000000..a6cc6bbb7 --- /dev/null +++ b/modules/pseudoclients/hs_main.cpp @@ -0,0 +1,85 @@ +/* HostServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 HostServCore : public Module +{ + public: + HostServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE) + { + this->SetAuthor("Anope"); + + if (!ircd || !ircd->vhost) + throw ModuleException("Your IRCd does not suppor vhosts"); + + BotInfo *HostServ = findbot(Config->HostServ); + if (HostServ == NULL) + throw ModuleException("No bot named " + Config->HostServ); + + Implementation i[] = { I_OnNickIdentify, I_OnNickUpdate, I_OnPreHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + } + + void OnNickIdentify(User *u) + { + HostInfo *ho = NULL; + NickAlias *na = findnick(u->nick); + if (na && na->hostinfo.HasVhost()) + ho = &na->hostinfo; + else + { + na = findnick(u->Account()->display); + if (na && na->hostinfo.HasVhost()) + ho = &na->hostinfo; + } + if (ho == NULL) + return; + + if (u->vhost.empty() || !u->vhost.equals_cs(na->hostinfo.GetHost()) || (!na->hostinfo.GetIdent().empty() && !u->GetVIdent().equals_cs(na->hostinfo.GetIdent()))) + { + ircdproto->SendVhost(u, na->hostinfo.GetIdent(), na->hostinfo.GetHost()); + if (ircd->vhost) + { + u->vhost = na->hostinfo.GetHost(); + u->UpdateHost(); + } + if (ircd->vident && !na->hostinfo.GetIdent().empty()) + u->SetVIdent(na->hostinfo.GetIdent()); + + BotInfo *bi = findbot(Config->HostServ); + if (bi) + { + if (!na->hostinfo.GetIdent().empty()) + u->SendMessage(bi, _("Your vhost of \002%s\002@\002%s\002 is now activated."), na->hostinfo.GetIdent().c_str(), na->hostinfo.GetHost().c_str()); + else + u->SendMessage(bi, _("Your vhost of \002%s\002 is now activated."), na->hostinfo.GetHost().c_str()); + } + } + } + + void OnNickUpdate(User *u) + { + this->OnNickIdentify(u); + } + + void OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->HostServ) + return; + source.Reply(_("%s commands:\n"), Config->HostServ.c_str()); + } +}; + +MODULE_INIT(HostServCore) + diff --git a/modules/pseudoclients/memoserv.h b/modules/pseudoclients/memoserv.h new file mode 100644 index 000000000..0bf0c79ca --- /dev/null +++ b/modules/pseudoclients/memoserv.h @@ -0,0 +1,41 @@ +#ifndef MEMOSERV_H +#define MEMOSERV_H + +class MemoServService : public Service +{ + public: + enum MemoResult + { + MEMO_SUCCESS, + MEMO_INVALID_TARGET, + MEMO_TOO_FAST, + MEMO_TARGET_FULL + }; + + MemoServService(Module *m) : Service(m, "MemoServ") { } + + /** Retrieve the memo info for a nick or channel + * @param target Target + * @param ischan Set to true if target is a channel + * @return A memoinfo structure or NULL + */ + virtual MemoInfo *GetMemoInfo(const Anope::string &target, bool &ischan) = 0; + + /** Sends a memo. + * @param source The source of the memo, can be anythin. + * @param target The target of the memo, nick or channel. + * @param message Memo text + * @param force true to force the memo, restrictions/delays etc are not checked + */ + virtual MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force = false) = 0; + + /** Check for new memos and notify the user if there are any + * @param u The user + */ + virtual void Check(User *u) = 0; +}; + +static service_reference<MemoServService> memoserv("MemoServ"); + +#endif // MEMOSERV_H + diff --git a/modules/pseudoclients/ms_main.cpp b/modules/pseudoclients/ms_main.cpp new file mode 100644 index 000000000..bb6d7847e --- /dev/null +++ b/modules/pseudoclients/ms_main.cpp @@ -0,0 +1,228 @@ +/* MemoServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 "memoserv.h" + +static BotInfo *MemoServ; + +static bool SendMemoMail(NickCore *nc, MemoInfo *mi, Memo *m) +{ + Anope::string message = Anope::printf(translate(nc, _( + "Hi %s\n" + " \n" + "You've just received a new memo from %s. This is memo number %d.\n" + " \n" + "Memo text:\n" + " \n" + "%s")), nc->display.c_str(), m->sender.c_str(), mi->GetIndex(m), m->text.c_str()); + + return Mail(nc, translate(nc, _("New memo")), message); +} + +class MyMemoServService : public MemoServService +{ + public: + MyMemoServService(Module *m) : MemoServService(m) { } + + MemoInfo *GetMemoInfo(const Anope::string &target, bool &ischan) + { + if (!target.empty() && target[0] == '#') + { + ischan = true; + ChannelInfo *ci = cs_findchan(target); + if (ci != NULL) + return &ci->memos; + } + else + { + ischan = false; + NickAlias *na = findnick(target); + if (na != NULL) + return &na->nc->memos; + } + + return NULL; + } + + MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force) + { + bool ischan; + MemoInfo *mi = this->GetMemoInfo(target, ischan); + + if (mi == NULL) + return MEMO_INVALID_TARGET; + + User *sender = finduser(source); + if (sender != NULL && !sender->HasPriv("memoserv/no-limit") && !force) + { + if (Config->MSSendDelay > 0 && sender->lastmemosend + Config->MSSendDelay > Anope::CurTime) + return MEMO_TOO_FAST; + else if (!mi->memomax) + return MEMO_TARGET_FULL; + else if (mi->memomax > 0 && mi->memos.size() >= mi->memomax) + return MEMO_TARGET_FULL; + else if (mi->HasIgnore(sender)) + return MEMO_SUCCESS; + } + + if (sender != NULL) + sender->lastmemosend = Anope::CurTime; + + Memo *m = new Memo(); + mi->memos.push_back(m); + m->sender = source; + m->time = Anope::CurTime; + m->text = message; + m->SetFlag(MF_UNREAD); + + FOREACH_MOD(I_OnMemoSend, OnMemoSend(source, target, mi, m)); + + if (ischan) + { + ChannelInfo *ci = cs_findchan(target); + + if (ci->c) + { + for (CUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) + { + UserContainer *cu = *it; + + if (ci->HasPriv(cu->user, CA_MEMO)) + { + if (cu->user->Account() && cu->user->Account()->HasFlag(NI_MEMO_RECEIVE)) + cu->user->SendMessage(MemoServ, MEMO_NEW_X_MEMO_ARRIVED, ci->name.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), ci->name.c_str(), mi->memos.size()); + } + } + } + } + else + { + NickCore *nc = findnick(target)->nc; + + if (nc->HasFlag(NI_MEMO_RECEIVE)) + { + for (std::list<NickAlias *>::iterator it = nc->aliases.begin(), it_end = nc->aliases.end(); it != it_end; ++it) + { + NickAlias *na = *it; + User *user = finduser(na->nick); + if (user && user->IsIdentified()) + user->SendMessage(MemoServ, MEMO_NEW_MEMO_ARRIVED, source.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), mi->memos.size()); + } + } + + /* let's get out the mail if set in the nickcore - certus */ + if (nc->HasFlag(NI_MEMO_MAIL)) + SendMemoMail(nc, mi, m); + } + + return MEMO_SUCCESS; + } + + void Check(User *u) + { + NickCore *nc = u->Account(); + if (!nc) + return; + + unsigned i = 0, end = nc->memos.memos.size(), newcnt = 0; + for (; i < end; ++i) + { + if (nc->memos.memos[i]->HasFlag(MF_UNREAD)) + ++newcnt; + } + if (newcnt > 0) + u->SendMessage(MemoServ, newcnt == 1 ? _("You have 1 new memo.") : _("You have %d new memos."), newcnt); + if (nc->memos.memomax > 0 && nc->memos.memos.size() >= nc->memos.memomax) + { + if (nc->memos.memos.size() > nc->memos.memomax) + u->SendMessage(MemoServ, _("You are over your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->memos.memomax); + else + u->SendMessage(MemoServ, _("You have reached your maximum number of memos (%d). You will be unable to receive any new memos until you delete some of your current ones."), nc->memos.memomax); + } + } +}; + +class MemoServCore : public Module +{ + MyMemoServService mymemoserv; + public: + MemoServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + mymemoserv(this) + { + this->SetAuthor("Anope"); + + MemoServ = findbot(Config->MemoServ); + if (MemoServ == NULL) + throw ModuleException("No bot named " + Config->MemoServ); + + Implementation i[] = { I_OnNickIdentify, I_OnJoinChannel, I_OnUserAway, I_OnNickUpdate, I_OnPreHelp, I_OnPostHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->mymemoserv); + } + + void OnNickIdentify(User *u) + { + this->mymemoserv.Check(u); + } + + void OnJoinChannel(User *u, Channel *c) + { + if (c->ci && c->ci->HasPriv(u, CA_MEMO) && c->ci->memos.memos.size() > 0) + { + if (c->ci->memos.memos.size() == 1) + u->SendMessage(MemoServ, _("There is \002%d\002 memo on channel %s."), c->ci->memos.memos.size(), c->ci->name.c_str()); + else + u->SendMessage(MemoServ, _("There are \002%d\002 memos on channel %s."), c->ci->memos.memos.size(), c->ci->name.c_str()); + } + } + + void OnUserAway(User *u, const Anope::string &message) + { + if (message.empty()) + this->mymemoserv.Check(u); + } + + void OnNickUpdate(User *u) + { + this->mymemoserv.Check(u); + } + + void OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->MemoServ) + return; + source.Reply(_("\002%s\002 is a utility allowing IRC users to send short\n" + "messages to other IRC users, whether they are online at\n" + "the time or not, or to channels(*). Both the sender's\n" + "nickname and the target nickname or channel must be\n" + "registered in order to send a memo.\n" + "%s's commands include:"), Config->MemoServ.c_str(), Config->MemoServ.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->MemoServ) + return; + source.Reply(_(" \n" + "Type \002%s%s HELP \037command\037\002 for help on any of the\n" + "above commands.\n" + "(*) By default, any user with at least level 10 access on a\n" + " channel can read that channel's memos. This can be\n" + " changed with the %s \002LEVELS\002 command."), Config->UseStrictPrivMsgString.c_str(), Config->MemoServ.c_str(), Config->ChanServ.c_str()); + } +}; + +MODULE_INIT(MemoServCore) + diff --git a/modules/pseudoclients/nickserv.h b/modules/pseudoclients/nickserv.h new file mode 100644 index 000000000..b58dbd65f --- /dev/null +++ b/modules/pseudoclients/nickserv.h @@ -0,0 +1,15 @@ +#ifndef NICKSERV_H +#define NICKSERV_H + +class NickServService : public Service +{ + public: + NickServService(Module *m) : Service(m, "NickServ") { } + + virtual void Validate(User *u) = 0; +}; + +static service_reference<NickServService> nickserv("NickServ"); + +#endif // NICKSERV_H + diff --git a/modules/pseudoclients/ns_main.cpp b/modules/pseudoclients/ns_main.cpp new file mode 100644 index 000000000..57323d0a5 --- /dev/null +++ b/modules/pseudoclients/ns_main.cpp @@ -0,0 +1,307 @@ +/* NickServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 "nickserv.h" + +static BotInfo *NickServ; + +class MyNickServService : public NickServService +{ + public: + MyNickServService(Module *m) : NickServService(m) { } + + void Validate(User *u) + { + NickAlias *na = findnick(u->nick); + if (!na) + return; + + if (na->nc->HasFlag(NI_SUSPENDED)) + { + u->SendMessage(NickServ, NICK_X_SUSPENDED, u->nick.c_str()); + u->Collide(na); + return; + } + + if (!u->IsIdentified() && !u->fingerprint.empty() && na->nc->FindCert(u->fingerprint)) + { + u->SendMessage(NickServ, _("SSL Fingerprint accepted, you are now identified.")); + u->Identify(na); + return; + } + + if (!na->nc->HasFlag(NI_SECURE) && u->IsRecognized()) + { + na->last_seen = Anope::CurTime; + Anope::string last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost(); + na->last_usermask = last_usermask; + na->last_realname = u->realname; + return; + } + + if (u->IsRecognized() || !na->nc->HasFlag(NI_KILL_IMMED)) + { + if (na->nc->HasFlag(NI_SECURE)) + u->SendMessage(NickServ, NICK_IS_SECURE, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + else + u->SendMessage(NickServ, NICK_IS_REGISTERED, Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + } + + if (na->nc->HasFlag(NI_KILLPROTECT) && !u->IsRecognized()) + { + if (na->nc->HasFlag(NI_KILL_IMMED)) + { + u->SendMessage(NickServ, FORCENICKCHANGE_NOW); + u->Collide(na); + } + else if (na->nc->HasFlag(NI_KILL_QUICK)) + { + u->SendMessage(NickServ, _("If you do not change within 20 seconds, I will change your nick.")); + new NickServCollide(u, 20); + } + else + { + u->SendMessage(NickServ, _("If you do not change within one minute, I will change your nick.")); + new NickServCollide(u, 60); + } + } + + } +}; + +class ExpireCallback : public CallBack +{ + public: + ExpireCallback(Module *owner) : CallBack(owner, Config->ExpireTimeout, Anope::CurTime, true) { } + + void Tick(time_t) + { + if (noexpire || readonly) + return; + + for (nickalias_map::const_iterator it = NickAliasList.begin(), it_end = NickAliasList.end(); it != it_end; ) + { + NickAlias *na = it->second; + ++it; + + User *u = finduser(na->nick); + if (u && (na->nc->HasFlag(NI_SECURE) ? u->IsIdentified(true) : u->IsRecognized(true))) + na->last_seen = Anope::CurTime; + + bool expire = false; + + if (na->nc->HasFlag(NI_UNCONFIRMED)) + if (Config->NSUnconfirmedExpire && Anope::CurTime - na->time_registered >= Config->NSUnconfirmedExpire) + expire = true; + if (na->nc->HasFlag(NI_SUSPENDED)) + { + if (Config->NSSuspendExpire && Anope::CurTime - na->last_seen >= Config->NSSuspendExpire) + expire = true; + } + else if (Config->NSExpire && Anope::CurTime - na->last_seen >= Config->NSExpire) + expire = true; + if (na->HasFlag(NS_NO_EXPIRE)) + expire = false; + + if (expire) + { + EventReturn MOD_RESULT; + FOREACH_RESULT(I_OnPreNickExpire, OnPreNickExpire(na)); + if (MOD_RESULT == EVENT_STOP) + continue; + Anope::string extra; + if (na->nc->HasFlag(NI_SUSPENDED)) + extra = "suspended "; + Log(LOG_NORMAL, "expire") << "Expiring " << extra << "nickname " << na->nick << " (group: " << na->nc->display << ") (e-mail: " << (na->nc->email.empty() ? "none" : na->nc->email) << ")"; + FOREACH_MOD(I_OnNickExpire, OnNickExpire(na)); + delete na; + } + } + } +}; + +class NickServCore : public Module +{ + MyNickServService mynickserv; + ExpireCallback expires; + + public: + NickServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), mynickserv(this), expires(this) + { + this->SetAuthor("Anope"); + + NickServ = findbot(Config->NickServ); + if (NickServ == NULL) + throw ModuleException("No bot named " + Config->NickServ); + + Implementation i[] = { I_OnDelNick, I_OnDelCore, I_OnChangeCoreDisplay, I_OnNickIdentify, I_OnNickGroup, + I_OnNickUpdate, I_OnUserNickChange, I_OnPreHelp, I_OnPostHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&this->mynickserv); + } + + void OnDelNick(NickAlias *na) + { + User *u = finduser(na->nick); + if (u && u->Account() == na->nc) + { + u->RemoveMode(NickServ, UMODE_REGISTERED); + ircdproto->SendAccountLogout(u, u->Account()); + ircdproto->SendUnregisteredNick(u); + u->Logout(); + } + } + + void OnDelCore(NickCore *nc) + { + Log(NickServ, "nick") << "deleting nickname group " << nc->display; + + /* Clean up this nick core from any users online using it + * (ones that /nick but remain unidentified) + */ + for (std::list<User *>::iterator it = nc->Users.begin(); it != nc->Users.end();) + { + User *user = *it++; + ircdproto->SendAccountLogout(user, user->Account()); + user->RemoveMode(NickServ, UMODE_REGISTERED); + ircdproto->SendUnregisteredNick(user); + user->Logout(); + FOREACH_MOD(I_OnNickLogout, OnNickLogout(user)); + } + nc->Users.clear(); + } + + void OnChangeCoreDisplay(NickCore *nc, const Anope::string &newdisplay) + { + Log(LOG_NORMAL, "nick", NickServ) << "Changing " << nc->display << " nickname group display to " << newdisplay; + } + + void OnNickIdentify(User *u) + { + NickAlias *this_na = findnick(u->nick); + if (this_na && this_na->nc == u->Account() && u->Account()->HasFlag(NI_UNCONFIRMED) == false) + u->SetMode(NickServ, UMODE_REGISTERED); + + if (Config->NSModeOnID) + for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it) + { + ChannelContainer *cc = *it; + Channel *c = cc->chan; + if (c) + chan_set_correct_modes(u, c, 1); + } + + if (Config->NSForceEmail && u->Account()->email.empty()) + { + u->SendMessage(NickServ, _("You must now supply an e-mail for your nick.\n" + "This e-mail will allow you to retrieve your password in\n" + "case you forget it.")); + u->SendMessage(NickServ, _("Type \002%s%s SET EMAIL \037e-mail\037\002 in order to set your e-mail.\n" + "Your privacy is respected; this e-mail won't be given to\n" + "any third-party person."), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str()); + } + + if (u->Account()->HasFlag(NI_UNCONFIRMED)) + { + u->SendMessage(NickServ, _("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you when you registered.")); + this_na = findnick(u->Account()->display); + time_t time_registered = Anope::CurTime - this_na->time_registered; + if (Config->NSUnconfirmedExpire > time_registered) + u->SendMessage(NickServ, _("Your account will expire, if not confirmed, in %s"), duration(Config->NSUnconfirmedExpire - time_registered).c_str()); + } + } + + void OnNickGroup(User *u, NickAlias *target) + { + if (target->nc->HasFlag(NI_UNCONFIRMED) == false) + u->SetMode(NickServ, UMODE_REGISTERED); + } + + void OnNickUpdate(User *u) + { + for (UChannelList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it) + { + ChannelContainer *cc = *it; + Channel *c = cc->chan; + if (c) + chan_set_correct_modes(u, c, 1); + } + } + + void OnUserNickChange(User *u, const Anope::string &oldnick) + { + NickAlias *na = findnick(u->nick); + /* If the new nick isnt registerd or its registerd and not yours */ + if (!na || na->nc != u->Account()) + { + u->RemoveMode(NickServ, UMODE_REGISTERED); + ircdproto->SendUnregisteredNick(u); + + this->mynickserv.Validate(u); + } + else + { + if (na->nc->HasFlag(NI_UNCONFIRMED) == false) + { + u->SetMode(NickServ, UMODE_REGISTERED); + ircdproto->SetAutoIdentificationToken(u); + } + Log(NickServ) << u->GetMask() << " automatically identified for group " << u->Account()->display; + } + } + + void OnUserModeSet(User *u, UserModeName Name) + { + if (Name == UMODE_REGISTERED && !u->IsIdentified()) + u->RemoveMode(NickServ, Name); + } + + void OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->NickServ) + return; + source.Reply(_("\002%s\002 allows you to \"register\" a nickname and\n" + "prevent others from using it. The following\n" + "commands allow for registration and maintenance of\n" + "nicknames; to use them, type \002%s%s \037command\037\002.\n" + "For more information on a specific command, type\n" + "\002%s%s %s \037command\037\002.\n "), Config->NickServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), Config->UseStrictPrivMsgString.c_str(), Config->NickServ.c_str(), source.command.c_str()); + } + + void OnPostHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->NickServ) + return; + if (source.u->IsServicesOper()) + source.Reply(_(" \n" + "Services Operators can also drop any nickname without needing\n" + "to identify for the nick, and may view the access list for\n" + "any nickname.")); + if (Config->NSExpire >= 86400) + source.Reply(_("Nicknames that are not used anymore are subject to \n" + "the automatic expiration, i.e. they will be deleted\n" + "after %d days if not used."), Config->NSExpire / 86400); + source.Reply(_(" \n" + "\002NOTICE:\002 This service is intended to provide a way for\n" + "IRC users to ensure their identity is not compromised.\n" + "It is \002NOT\002 intended to facilitate \"stealing\" of\n" + "nicknames or other malicious actions. Abuse of %s\n" + "will result in, at minimum, loss of the abused\n" + "nickname(s)."), Config->NickServ.c_str()); + } +}; + +MODULE_INIT(NickServCore) + diff --git a/modules/pseudoclients/os_main.cpp b/modules/pseudoclients/os_main.cpp new file mode 100644 index 000000000..b555b9ede --- /dev/null +++ b/modules/pseudoclients/os_main.cpp @@ -0,0 +1,371 @@ +/* OperServ core functions + * + * (C) 2003-2011 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 BotInfo *OperServ; + +class SGLineManager : public XLineManager +{ + public: + SGLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sgline", 'G') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + Anope::string realreason = reason; + if (!creator.empty() && Config->AddAkiller) + realreason = "[" + creator + "] " + reason; + + XLine *x = new XLine(mask, creator, expires, realreason); + + this->AddXLine(x); + + if (UplinkSock && Config->AkillOnAdd) + this->Send(NULL, x); + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendAkillDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + u->Kill(Config->OperServ, x->Reason); + ircdproto->SendAkill(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallAkillExpire) + ircdproto->SendGlobops(OperServ, "AKILL on %s has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendAkill(u, x); + } +}; + +class SZLineManager : public XLineManager +{ + public: + SZLineManager(Module *creator) : XLineManager(creator, "xlinemanager/szline", 'Z') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + XLine *x = new XLine(mask, creator, expires, reason); + + this->AddXLine(x); + + if (UplinkSock) + this->Send(NULL, x); + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendSZLineDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + { + Anope::string reason = "Z-Lined: " + x->Reason; + u->Kill(Config->OperServ, reason); + } + + ircdproto->SendSZLine(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallSZLineExpire) + ircdproto->SendGlobops(OperServ, "SZLINE on \2%s\2 has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendSZLine(u, x); + } +}; + +class SQLineManager : public XLineManager +{ + public: + SQLineManager(Module *creator) : XLineManager(creator, "xlinemanager/sqline", 'Q') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + XLine *x = new XLine(mask, creator, expires, reason); + + this->AddXLine(x); + + if (Config->KillonSQline) + { + Anope::string rreason = "Q-Lined: " + reason; + + if (mask[0] == '#') + { + for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) + { + Channel *c = cit->second; + + if (!Anope::Match(c->name, mask)) + continue; + for (CUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ) + { + UserContainer *uc = *it; + ++it; + + if (uc->user->HasMode(UMODE_OPER) || uc->user->server == Me) + continue; + c->Kick(NULL, uc->user, "%s", reason.c_str()); + } + } + } + else + { + for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) + { + User *user = it->second; + ++it; + + if (!user->HasMode(UMODE_OPER) && user->server != Me && Anope::Match(user->nick, x->Mask)) + user->Kill(Config->ServerName, rreason); + } + } + } + + if (UplinkSock) + this->Send(NULL, x); + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendSQLineDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + { + Anope::string reason = "Q-Lined: " + x->Reason; + u->Kill(Config->OperServ, reason); + } + + this->Send(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallSQLineExpire) + ircdproto->SendGlobops(OperServ, "SQLINE on \2%s\2 has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendSQLine(u, x); + } + + bool CheckChannel(Channel *c) + { + if (ircd->chansqline) + for (std::vector<XLine *>::const_iterator it = this->GetList().begin(), it_end = this->GetList().end(); it != it_end; ++it) + if (Anope::Match(c->name, (*it)->Mask)) + return true; + return false; + } +}; + +class SNLineManager : public XLineManager +{ + public: + SNLineManager(Module *creator) : XLineManager(creator, "xlinemanager/snline", 'N') { } + + XLine *Add(const Anope::string &mask, const Anope::string &creator, time_t expires, const Anope::string &reason) + { + XLine *x = new XLine(mask, creator, expires, reason); + + this->AddXLine(x); + + if (Config->KillonSNline && !ircd->sglineenforce) + { + Anope::string rreason = "G-Lined: " + reason; + + for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();) + { + User *user = it->second; + ++it; + + if (!user->HasMode(UMODE_OPER) && user->server != Me && Anope::Match(user->realname, x->Mask)) + user->Kill(Config->ServerName, rreason); + } + } + + return x; + } + + void Del(XLine *x) + { + ircdproto->SendSGLineDel(x); + } + + void OnMatch(User *u, XLine *x) + { + if (u) + { + Anope::string reason = "G-Lined: " + x->Reason; + u->Kill(Config->OperServ, reason); + } + this->Send(u, x); + } + + void OnExpire(XLine *x) + { + if (Config->WallSNLineExpire) + ircdproto->SendGlobops(OperServ, "SNLINE on \2%s\2 has expired", x->Mask.c_str()); + } + + void Send(User *u, XLine *x) + { + ircdproto->SendSGLine(u, x); + } + + XLine *Check(User *u) + { + for (unsigned i = this->XLines.size(); i > 0; --i) + { + XLine *x = this->XLines[i - 1]; + + if (x->Expires && x->Expires < Anope::CurTime) + { + this->OnExpire(x); + this->Del(x); + delete x; + this->XLines.erase(XLines.begin() + i - 1); + continue; + } + + if (Anope::Match(u->realname, x->Mask)) + { + this->OnMatch(u, x); + return x; + } + } + + return NULL; + } +}; + +class OperServCore : public Module +{ + SGLineManager sglines; + SZLineManager szlines; + SQLineManager sqlines; + SNLineManager snlines; + + public: + OperServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE), + sglines(this), szlines(this), sqlines(this), snlines(this) + { + this->SetAuthor("Anope"); + + OperServ = findbot(Config->OperServ); + if (OperServ == NULL) + throw ModuleException("No bot named " + Config->OperServ); + + Implementation i[] = { I_OnBotPrivmsg, I_OnServerQuit, I_OnUserModeSet, I_OnUserModeUnset, I_OnUserConnect, I_OnUserNickChange, I_OnPreHelp }; + ModuleManager::Attach(i, this, sizeof(i) / sizeof(Implementation)); + + ModuleManager::RegisterService(&sglines); + ModuleManager::RegisterService(&szlines); + ModuleManager::RegisterService(&sqlines); + ModuleManager::RegisterService(&snlines); + + /* Yes, these are in this order for a reason. Most violent->least violent. */ + XLineManager::RegisterXLineManager(&sglines); + XLineManager::RegisterXLineManager(&szlines); + XLineManager::RegisterXLineManager(&sqlines); + XLineManager::RegisterXLineManager(&snlines); + } + + EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) + { + if (Config->OSOpersOnly && !u->HasMode(UMODE_OPER) && bi->nick == Config->OperServ) + { + u->SendMessage(bi, ACCESS_DENIED); + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } + + void OnServerQuit(Server *server) + { + if (server->HasFlag(SERVER_JUPED)) + ircdproto->SendGlobops(OperServ, "Received SQUIT for juped server %s", server->GetName().c_str()); + } + + void OnUserModeSet(User *u, UserModeName Name) + { + if (Name == UMODE_OPER) + { + if (Config->WallOper) + ircdproto->SendGlobops(OperServ, "\2%s\2 is now an IRC operator.", u->nick.c_str()); + Log(OperServ) << u->nick << " is now an IRC operator"; + } + } + + void OnUserModeUnset(User *u, UserModeName Name) + { + if (Name == UMODE_OPER) + Log(OperServ) << u->nick << " is no longer an IRC operator"; + } + + void OnUserConnect(dynamic_reference<User> &u, bool &exempt) + { + if (u && !exempt) + XLineManager::CheckAll(u); + } + + void OnUserNickChange(User *u, const Anope::string &oldnick) + { + if (ircd->sqline && !u->HasMode(UMODE_OPER)) + this->sqlines.Check(u); + } + + EventReturn OnCheckKick(User *u, ChannelInfo *ci, bool &kick) + { + if (this->sqlines.CheckChannel(ci->c)) + kick = true; + return EVENT_CONTINUE; + } + + void OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) + { + if (!params.empty() || source.owner->nick != Config->OperServ) + return; + source.Reply(_("%s commands:"), Config->OperServ.c_str()); + } +}; + +MODULE_INIT(OperServCore) + |
