diff options
Diffstat (limited to 'modules/commands')
38 files changed, 2633 insertions, 1676 deletions
diff --git a/modules/commands/bs_assign.cpp b/modules/commands/bs_assign.cpp index 7b241f09e..ba925ad80 100644 --- a/modules/commands/bs_assign.cpp +++ b/modules/commands/bs_assign.cpp @@ -145,14 +145,69 @@ class CommandBSUnassign : public Command } }; +class CommandBSSetNoBot : public Command +{ + public: + CommandBSSetNoBot(Module *creator, const Anope::string &sname = "botserv/set/nobot") : Command(creator, sname, 2, 2) + { + this->SetDesc(_("Prevent a bot from being assigned to a channel")); + this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + const Anope::string &value = params[1]; + + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (value.equals_ci("ON")) + { + Log(LOG_ADMIN, source, this, ci) << "to enable nobot"; + + ci->Extend<bool>("BS_NOBOT"); + if (ci->bi) + ci->bi->UnAssign(source.GetUser(), ci); + source.Reply(_("No-bot mode is now \002on\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + Log(LOG_ADMIN, source, this, ci) << "to disable nobot"; + + ci->Shrink<bool>("BS_NOBOT"); + source.Reply(_("No-bot mode is now \002off\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, source.command); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(_(" \n" + "This option makes a channel be unassignable. If a bot\n" + "is already assigned to the channel, it is unassigned\n" + "automatically when you enable the option.")); + return true; + } +}; + class BSAssign : public Module { + ExtensibleItem<bool> nobot; + CommandBSAssign commandbsassign; CommandBSUnassign commandbsunassign; + CommandBSSetNoBot commandbssetnobot; public: BSAssign(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandbsassign(this), commandbsunassign(this) + nobot(this, "BS_NOBOT"), + commandbsassign(this), commandbsunassign(this), commandbssetnobot(this) { } @@ -163,7 +218,7 @@ class BSAssign : public Module return; AccessGroup access = c->ci->AccessFor(source); - if (c->ci->HasExt("BS_NOBOT") || (!access.HasPriv("ASSIGN") && !source->HasPriv("botserv/administration"))) + if (nobot.HasExt(c->ci) || (!access.HasPriv("ASSIGN") && !source->HasPriv("botserv/administration"))) { targ->SendMessage(bi, ACCESS_DENIED); return; @@ -184,6 +239,12 @@ class BSAssign : public Module bi->Assign(source, c->ci); targ->SendMessage(bi, _("Bot \002%s\002 has been assigned to %s."), bi->nick.c_str(), c->name.c_str()); } + + void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override + { + if (nobot.HasExt(ci)) + info.AddOption(_("No bot")); + } }; MODULE_INIT(BSAssign) diff --git a/modules/commands/bs_badwords.cpp b/modules/commands/bs_badwords.cpp index 22fa74714..ff9707778 100644 --- a/modules/commands/bs_badwords.cpp +++ b/modules/commands/bs_badwords.cpp @@ -10,11 +10,132 @@ */ #include "module.h" +#include "modules/bs_badwords.h" + +struct BadWordImpl : BadWord, Serializable +{ + BadWordImpl() : Serializable("BadWord") { } + ~BadWordImpl(); + + void Serialize(Serialize::Data &data) const anope_override + { + data["ci"] << this->chan; + data["word"] << this->word; + data.SetType("type", Serialize::Data::DT_INT); data["type"] << this->type; + } + + static Serializable* Unserialize(Serializable *obj, Serialize::Data &); +}; + +struct BadWordsImpl : BadWords +{ + Serialize::Reference<ChannelInfo> ci; + typedef std::vector<BadWordImpl *> list; + Serialize::Checker<list> badwords; + + BadWordsImpl(Extensible *obj) : ci(anope_dynamic_static_cast<ChannelInfo *>(obj)), badwords("BadWord") { } + + BadWord* AddBadWord(const Anope::string &word, BadWordType type) anope_override + { + BadWordImpl *bw = new BadWordImpl(); + bw->chan = ci->name; + bw->word = word; + bw->type = type; + + this->badwords->push_back(bw); + + FOREACH_MOD(OnBadWordAdd, (ci, bw)); + + return bw; + } + + BadWord* GetBadWord(unsigned index) const anope_override + { + if (this->badwords->empty() || index >= this->badwords->size()) + return NULL; + + BadWordImpl *bw = (*this->badwords)[index]; + bw->QueueUpdate(); + return bw; + } + + unsigned GetBadWordCount() const anope_override + { + return this->badwords->size(); + } + + void EraseBadWord(unsigned index) anope_override + { + if (this->badwords->empty() || index >= this->badwords->size()) + return; + + FOREACH_MOD(OnBadWordDel, (ci, (*this->badwords)[index])); + + delete this->badwords->at(index); + } + + void ClearBadWords() anope_override + { + while (!this->badwords->empty()) + delete this->badwords->back(); + } + + void Check() anope_override + { + if (this->badwords->empty()) + ci->Shrink<BadWords>("badwords"); + } +}; + +BadWordImpl::~BadWordImpl() +{ + ChannelInfo *ci = ChannelInfo::Find(chan); + if (ci) + { + BadWordsImpl *badwords = ci->GetExt<BadWordsImpl>("badwords"); + if (badwords) + { + BadWordsImpl::list::iterator it = std::find(badwords->badwords->begin(), badwords->badwords->end(), this); + if (it != badwords->badwords->end()) + badwords->badwords->erase(it); + } + } +} + +Serializable* BadWordImpl::Unserialize(Serializable *obj, Serialize::Data &data) +{ + Anope::string sci, sword; + + data["ci"] >> sci; + data["word"] >> sword; + + ChannelInfo *ci = ChannelInfo::Find(sci); + if (!ci) + return NULL; + + unsigned int n; + data["type"] >> n; + + BadWordImpl *bw; + if (obj) + bw = anope_dynamic_static_cast<BadWordImpl *>(obj); + else + bw = new BadWordImpl(); + bw->chan = sci; + bw->word = sword; + bw->type = static_cast<BadWordType>(n); + + BadWordsImpl *bws = ci->Require<BadWordsImpl>("badwords"); + bws->badwords->push_back(bw); + + return bw; +} class BadwordsDelCallback : public NumberList { CommandSource &source; ChannelInfo *ci; + BadWords *bw; Command *c; unsigned deleted; bool override; @@ -23,6 +144,7 @@ class BadwordsDelCallback : public NumberList { if (!source.AccessFor(ci).HasPriv("BADWORDS") && source.HasPriv("botserv/administration")) this->override = true; + bw = ci->Require<BadWords>("badwords"); } ~BadwordsDelCallback() @@ -37,12 +159,12 @@ class BadwordsDelCallback : public NumberList void HandleNumber(unsigned Number) anope_override { - if (!Number || Number > ci->GetBadWordCount()) + if (!bw || !Number || Number > bw->GetBadWordCount()) return; - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "DEL " << ci->GetBadWord(Number - 1)->word; + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "DEL " << bw->GetBadWord(Number - 1)->word; ++deleted; - ci->EraseBadWord(Number - 1); + bw->EraseBadWord(Number - 1); } }; @@ -54,10 +176,11 @@ class CommandBSBadwords : public Command bool override = !source.AccessFor(ci).HasPriv("BADWORDS"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "LIST"; ListFormatter list; + BadWords *bw = ci->GetExt<BadWords>("badwords"); list.AddColumn("Number").AddColumn("Word").AddColumn("Type"); - if (!ci->GetBadWordCount()) + if (!bw || !bw->GetBadWordCount()) { source.Reply(_("%s bad words list is empty."), ci->name.c_str()); return; @@ -68,40 +191,41 @@ class CommandBSBadwords : public Command { ListFormatter &list; ChannelInfo *ci; + BadWords *bw; public: - BadwordsListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci) + BadwordsListCallback(ListFormatter &_list, ChannelInfo *_ci, BadWords *_bw, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci), bw(_bw) { } void HandleNumber(unsigned Number) anope_override { - if (!Number || Number > ci->GetBadWordCount()) + if (!Number || Number > bw->GetBadWordCount()) return; - const BadWord *bw = ci->GetBadWord(Number - 1); + const BadWord *b = bw->GetBadWord(Number - 1); ListFormatter::ListEntry entry; entry["Number"] = stringify(Number); - entry["Word"] = bw->word; - entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : "")); + entry["Word"] = b->word; + entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : "")); this->list.AddEntry(entry); } } - nl_list(list, ci, word); + nl_list(list, ci, bw, word); nl_list.Process(); } else { - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (unsigned i = 0, end = bw->GetBadWordCount(); i < end; ++i) { - const BadWord *bw = ci->GetBadWord(i); + const BadWord *b = bw->GetBadWord(i); - if (!word.empty() && !Anope::Match(bw->word, word)) + if (!word.empty() && !Anope::Match(b->word, word)) continue; ListFormatter::ListEntry entry; entry["Number"] = stringify(i + 1); - entry["Word"] = bw->word; - entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : "")); + entry["Word"] = b->word; + entry["Type"] = b->type == BW_SINGLE ? "(SINGLE)" : (b->type == BW_START ? "(START)" : (b->type == BW_END ? "(END)" : "")); list.AddEntry(entry); } } @@ -127,6 +251,7 @@ class CommandBSBadwords : public Command size_t pos = word.rfind(' '); BadWordType bwtype = BW_ANY; Anope::string realword = word; + BadWords *badwords = ci->Require<BadWords>("badwords"); if (pos != Anope::string::npos) { @@ -144,7 +269,7 @@ class CommandBSBadwords : public Command } unsigned badwordsmax = Config->GetModule(this->module)->Get<unsigned>("badwordsmax"); - if (ci->GetBadWordCount() >= badwordsmax) + if (badwords->GetBadWordCount() >= badwordsmax) { source.Reply(_("Sorry, you can only have %d bad words entries on a channel."), badwordsmax); return; @@ -152,9 +277,9 @@ class CommandBSBadwords : public Command bool casesensitive = Config->GetModule("botserv")->Get<bool>("casesensitive"); - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (unsigned i = 0, end = badwords->GetBadWordCount(); i < end; ++i) { - const BadWord *bw = ci->GetBadWord(i); + const BadWord *bw = badwords->GetBadWord(i); if ((casesensitive && realword.equals_cs(bw->word)) || (!casesensitive && realword.equals_ci(bw->word))) { @@ -165,13 +290,21 @@ class CommandBSBadwords : public Command bool override = !source.AccessFor(ci).HasPriv("BADWORDS"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "ADD " << realword; - ci->AddBadWord(realword, bwtype); + badwords->AddBadWord(realword, bwtype); source.Reply(_("\002%s\002 added to %s bad words list."), realword.c_str(), ci->name.c_str()); } void DoDelete(CommandSource &source, ChannelInfo *ci, const Anope::string &word) { + BadWords *badwords = ci->GetExt<BadWords>("badwords"); + + if (!badwords || !badwords->GetBadWordCount()) + { + source.Reply(_("%s bad words list is empty."), ci->name.c_str()); + return; + } + /* Special case: is it a number/list? Only do search if it isn't. */ if (!word.empty() && isdigit(word[0]) && word.find_first_not_of("1234567890,-") == Anope::string::npos) { @@ -183,9 +316,9 @@ class CommandBSBadwords : public Command unsigned i, end; const BadWord *badword; - for (i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (i = 0, end = badwords->GetBadWordCount(); i < end; ++i) { - badword = ci->GetBadWord(i); + badword = badwords->GetBadWord(i); if (word.equals_ci(badword->word)) break; @@ -202,10 +335,10 @@ class CommandBSBadwords : public Command source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str()); - ci->EraseBadWord(i); + badwords->EraseBadWord(i); } - return; + badwords->Check(); } void DoClear(CommandSource &source, ChannelInfo *ci) @@ -213,7 +346,9 @@ class CommandBSBadwords : public Command bool override = !source.AccessFor(ci).HasPriv("BADWORDS"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "CLEAR"; - ci->ClearBadWords(); + BadWords *badwords = ci->GetExt<BadWords>("badwords"); + if (badwords) + badwords->ClearBadWords(); source.Reply(_("Bad words list is now empty.")); } @@ -309,10 +444,12 @@ class CommandBSBadwords : public Command class BSBadwords : public Module { CommandBSBadwords commandbsbadwords; + ExtensibleItem<BadWordsImpl> badwords; + Serialize::Type badword_type; public: BSBadwords(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandbsbadwords(this) + commandbsbadwords(this), badwords(this, "badwords"), badword_type("BadWord", BadWordImpl::Unserialize) { } }; diff --git a/modules/commands/bs_info.cpp b/modules/commands/bs_info.cpp index 8bbfd0b18..a3eb9ce2d 100644 --- a/modules/commands/bs_info.cpp +++ b/modules/commands/bs_info.cpp @@ -36,16 +36,6 @@ class CommandBSInfo : public Command buffers.push_back(buf); } - void CheckOptStr(Anope::string &buf, const Anope::string &flag, const char *option, Extensible *flags, const NickCore *nc) - { - if (flags->HasExt(flag)) - { - if (!buf.empty()) - buf += ", "; - buf += Language::Translate(nc, option); - } - } - public: CommandBSInfo(Module *creator) : Command(creator, "botserv/info", 1, 1) { @@ -57,8 +47,8 @@ class CommandBSInfo : public Command { const Anope::string &query = params[0]; - const BotInfo *bi = BotInfo::Find(query, true); - ChannelInfo *ci; + BotInfo *bi = BotInfo::Find(query, true); + ChannelInfo *ci = ChannelInfo::Find(query); InfoFormatter info(source.nc); if (bi) @@ -70,6 +60,8 @@ class CommandBSInfo : public Command info[_("Options")] = bi->oper_only ? _("Private") : _("None"); info[_("Used on")] = stringify(bi->GetChannelCount()) + " channel(s)"; + FOREACH_MOD(OnBotInfo, (source, bi, ci, info)); + std::vector<Anope::string> replies; info.Process(replies); @@ -85,7 +77,7 @@ class CommandBSInfo : public Command } } - else if ((ci = ChannelInfo::Find(query))) + else if (ci) { if (!source.AccessFor(ci).HasPriv("INFO") && !source.HasPriv("botserv/administration")) { @@ -99,114 +91,7 @@ class CommandBSInfo : public Command Anope::string enabled = Language::Translate(source.nc, _("Enabled")); Anope::string disabled = Language::Translate(source.nc, _("Disabled")); - if (ci->HasExt("BS_KICK_BADWORDS")) - { - if (ci->ttb[TTB_BADWORDS]) - info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), ci->ttb[TTB_BADWORDS]); - else - info[_("Bad words kicker")] = enabled; - } - else - info[_("Bad words kicker")] = disabled; - - if (ci->HasExt("BS_KICK_BOLDS")) - { - if (ci->ttb[TTB_BOLDS]) - info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), ci->ttb[TTB_BOLDS]); - else - info[_("Bolds kicker")] = enabled; - } - else - info[_("Bolds kicker")] = disabled; - - if (ci->HasExt("BS_KICK_CAPS")) - { - if (ci->ttb[TTB_CAPS]) - info[_("Caps kicker")] = Anope::printf(_("%s (%d kick(s) to ban; minimum %d/%d%%"), enabled.c_str(), ci->ttb[TTB_CAPS], ci->capsmin, ci->capspercent); - else - info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), ci->capsmin, ci->capspercent); - } - else - info[_("Caps kicker")] = disabled; - - if (ci->HasExt("BS_KICK_COLORS")) - { - if (ci->ttb[TTB_COLORS]) - info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_COLORS]); - else - info[_("Colors kicker")] = enabled; - } - else - info[_("Colors kicker")] = disabled; - - if (ci->HasExt("BS_KICK_FLOOD")) - { - if (ci->ttb[TTB_FLOOD]) - info[_("Flood kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d lines in %ds"), enabled.c_str(), ci->ttb[TTB_FLOOD], ci->floodlines, ci->floodsecs); - else - info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), ci->floodlines, ci->floodsecs); - } - else - info[_("Flood kicker")] = disabled; - - if (ci->HasExt("BS_KICK_REPEAT")) - { - if (ci->ttb[TTB_REPEAT]) - info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), ci->ttb[TTB_REPEAT], ci->repeattimes); - else - info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), ci->repeattimes); - } - else - info[_("Repeat kicker")] = disabled; - - if (ci->HasExt("BS_KICK_REVERSES")) - { - if (ci->ttb[TTB_REVERSES]) - info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_REVERSES]); - else - info[_("Reverses kicker")] = enabled; - } - else - info[_("Reverses kicker")] = disabled; - - if (ci->HasExt("BS_KICK_UNDERLINES")) - { - if (ci->ttb[TTB_UNDERLINES]) - info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_UNDERLINES]); - else - info[_("Underlines kicker")] = enabled; - } - else - info[_("Underlines kicker")] = disabled; - - if (ci->HasExt("BS_KICK_ITALICS")) - { - if (ci->ttb[TTB_ITALICS]) - info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_ITALICS]); - else - info[_("Italics kicker")] = enabled; - } - else - info[_("Italics kicker")] = disabled; - - if (ci->HasExt("BS_KICK_AMSGS")) - { - if (ci->ttb[TTB_AMSGS]) - info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), ci->ttb[TTB_AMSGS]); - else - info[_("AMSG kicker")] = enabled; - } - else - info[_("AMSG kicker")] = disabled; - - Anope::string flags; - CheckOptStr(flags, "BS_DONTKICKOPS", _("Ops protection"), ci, source.nc); - CheckOptStr(flags, "BS_DONTKICKVOICES", _("Voices protection"), ci, source.nc); - CheckOptStr(flags, "BS_FANTASY", _("Fantasy"), ci, source.nc); - CheckOptStr(flags, "BS_GREET", _("Greet"), ci, source.nc); - CheckOptStr(flags, "BS_NOBOT", _("No bot"), ci, source.nc); - - info[_("Options")] = flags.empty() ? _("None") : flags; + FOREACH_MOD(OnBotInfo, (source, bi, ci, info)); std::vector<Anope::string> replies; info.Process(replies); diff --git a/modules/commands/bs_kick.cpp b/modules/commands/bs_kick.cpp index 50d4a0e5f..b22dd9ccb 100644 --- a/modules/commands/bs_kick.cpp +++ b/modules/commands/bs_kick.cpp @@ -12,9 +12,107 @@ */ #include "module.h" +#include "modules/bs_kick.h" +#include "modules/bs_badwords.h" static Module *me; +struct KickerDataImpl : KickerData +{ + KickerDataImpl(Extensible *obj) + { + amsgs = badwords = bolds = caps = colors = flood = italics = repeat = reverses = underlines = false; + for (int16_t i = 0; i < TTB_SIZE; ++i) + ttb[i] = 0; + capsmin = capspercent = 0; + floodlines = floodsecs = 0; + repeattimes = 0; + + dontkickops = dontkickvoices = false; + } + + void Check(ChannelInfo *ci) anope_override + { + if (amsgs || badwords || bolds || caps || colors || flood || italics || repeat || reverses || underlines) + return; + + ci->Shrink<KickerData>("kickerdata"); + } + + struct ExtensibleItem : ::ExtensibleItem<KickerDataImpl> + { + ExtensibleItem(Module *m, const Anope::string &name) : ::ExtensibleItem<KickerDataImpl>(m, name) { } + + void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override + { + if (s->GetSerializableType()->GetName() != "ChannelInfo") + return; + + const ChannelInfo *ci = anope_dynamic_static_cast<const ChannelInfo *>(e); + KickerData *kd = this->Get(ci); + if (kd == NULL) + return; + + data["kickerdata:amsgs"] << kd->amsgs; + data["kickerdata:badwords"] << kd->badwords; + data["kickerdata:bolds"] << kd->bolds; + data["kickerdata:caps"] << kd->caps; + data["kickerdata:colors"] << kd->colors; + data["kickerdata:flood"] << kd->flood; + data["kickerdata:italics"] << kd->italics; + data["kickerdata:repeat"] << kd->repeat; + data["kickerdata:reverses"] << kd->reverses; + data["kickerdata:underlines"] << kd->underlines; + + data.SetType("capsmin", Serialize::Data::DT_INT); data["capsmin"] << kd->capsmin; + data.SetType("capspercent", Serialize::Data::DT_INT); data["capspercent"] << kd->capspercent; + data.SetType("floodlines", Serialize::Data::DT_INT); data["floodlines"] << kd->floodlines; + data.SetType("floodsecs", Serialize::Data::DT_INT); data["floodsecs"] << kd->floodsecs; + data.SetType("repeattimes", Serialize::Data::DT_INT); data["repeattimes"] << kd->repeattimes; + for (int16_t i = 0; i < TTB_SIZE; ++i) + data["ttb"] << kd->ttb[i] << " "; + } + + void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override + { + if (s->GetSerializableType()->GetName() != "ChannelInfo") + return; + + ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(e); + KickerData *kd = ci->Require<KickerData>("kickerdata"); + + data["kickerdata:amsgs"] >> kd->amsgs; + data["kickerdata:badwords"] >> kd->badwords; + data["kickerdata:bolds"] >> kd->bolds; + data["kickerdata:caps"] >> kd->caps; + data["kickerdata:colors"] >> kd->colors; + data["kickerdata:flood"] >> kd->flood; + data["kickerdata:italics"] >> kd->italics; + data["kickerdata:repeat"] >> kd->repeat; + data["kickerdata:reverses"] >> kd->reverses; + data["kickerdata:underlines"] >> kd->underlines; + + data["capsmin"] >> kd->capsmin; + data["capspercent"] >> kd->capspercent; + data["floodlines"] >> kd->floodlines; + data["floodsecs"] >> kd->floodsecs; + data["repeattimes"] >> kd->repeattimes; + + Anope::string ttb, tok; + data["ttb"] >> ttb; + spacesepstream sep(ttb); + for (int i = 0; sep.GetToken(tok) && i < TTB_SIZE; ++i) + try + { + ttb[i] = convertTo<int16_t>(tok); + } + catch (const ConvertException &) { } + + kd->Check(ci); + } + }; +}; + class CommandBSKick : public Command { public: @@ -99,7 +197,7 @@ class CommandBSKickBase : public Command return false; } - void Process(CommandSource &source, ChannelInfo *ci, const Anope::string ¶m, const Anope::string &ttb, size_t ttb_idx, const Anope::string &optname, const Anope::string &mdname) + void Process(CommandSource &source, ChannelInfo *ci, const Anope::string ¶m, const Anope::string &ttb, size_t ttb_idx, const Anope::string &optname, KickerData *kd, bool &val) { if (param.equals_ci("ON")) { @@ -119,15 +217,15 @@ class CommandBSKickBase : public Command return; } - ci->ttb[ttb_idx] = i; + kd->ttb[ttb_idx] = i; } else - ci->ttb[ttb_idx] = 0; + kd->ttb[ttb_idx] = 0; - ci->ExtendMetadata(mdname); - if (ci->ttb[ttb_idx]) + val = true; + if (kd->ttb[ttb_idx]) source.Reply(_("Bot will now kick for \002%s\002, and will place a ban\n" - "after %d kicks for the same user."), optname.c_str(), ci->ttb[ttb_idx]); + "after %d kicks for the same user."), optname.c_str(), kd->ttb[ttb_idx]); else source.Reply(_("Bot will now kick for \002%s\002."), optname.c_str()); @@ -139,7 +237,7 @@ class CommandBSKickBase : public Command bool override = !source.AccessFor(ci).HasPriv("SET"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable the " << optname << "kicker"; - ci->Shrink(mdname); + val = false; source.Reply(_("Bot won't kick for \002%s\002 anymore."), optname.c_str()); } else @@ -160,7 +258,11 @@ class CommandBSKickAMSG : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_AMSGS, "AMSG", "BS_KICK_AMSGS"); + { + KickerData *kd = ci->Require<KickerData>("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_AMSGS, "AMSG", kd, kd->amsgs); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -188,7 +290,12 @@ class CommandBSKickBadwords : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BADWORDS, "badwords", "BS_KICK_BADWORDS"); + { + KickerData *kd = ci->Require<KickerData>("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BADWORDS, "badwords", kd, kd->badwords); + kd->Check(ci); + } + } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -219,7 +326,11 @@ class CommandBSKickBolds : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BOLDS, "bolds", "BS_KICK_BOLDS"); + { + KickerData *kd = ci->Require<KickerData>("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_BOLDS, "bolds", kd, kd->bolds); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -248,6 +359,8 @@ class CommandBSKickCaps : public CommandBSKickBase if (!CheckArguments(source, params, ci)) return; + KickerData *kd = ci->Require<KickerData>("kickerdata"); + if (params[1].equals_ci("ON")) { const Anope::string &ttb = params.size() > 2 ? params[2] : "", @@ -258,52 +371,54 @@ class CommandBSKickCaps : public CommandBSKickBase { try { - ci->ttb[TTB_CAPS] = convertTo<int16_t>(ttb); - if (ci->ttb[TTB_CAPS] < 0) + kd->ttb[TTB_CAPS] = convertTo<int16_t>(ttb); + if (kd->ttb[TTB_CAPS] < 0) throw ConvertException(); } catch (const ConvertException &) { - ci->ttb[TTB_CAPS] = 0; + kd->ttb[TTB_CAPS] = 0; source.Reply(_("\002%s\002 cannot be taken as times to ban."), ttb.c_str()); return; } } else - ci->ttb[TTB_CAPS] = 0; + kd->ttb[TTB_CAPS] = 0; - ci->capsmin = 10; + kd->capsmin = 10; try { - ci->capsmin = convertTo<int16_t>(min); + kd->capsmin = convertTo<int16_t>(min); } catch (const ConvertException &) { } - if (ci->capsmin < 1) - ci->capsmin = 10; + if (kd->capsmin < 1) + kd->capsmin = 10; - ci->capspercent = 25; + kd->capspercent = 25; try { - ci->capspercent = convertTo<int16_t>(percent); + kd->capspercent = convertTo<int16_t>(percent); } catch (const ConvertException &) { } - if (ci->capspercent < 1 || ci->capspercent > 100) - ci->capspercent = 25; + if (kd->capspercent < 1 || kd->capspercent > 100) + kd->capspercent = 25; - ci->ExtendMetadata("BS_KICK_CAPS"); - if (ci->ttb[TTB_CAPS]) + kd->caps = true; + if (kd->ttb[TTB_CAPS]) source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n" "%d characters and %d%% of the entire message), and will\n" - "place a ban after %d kicks for the same user."), ci->capsmin, ci->capspercent, ci->ttb[TTB_CAPS]); + "place a ban after %d kicks for the same user."), kd->capsmin, kd->capspercent, kd->ttb[TTB_CAPS]); else source.Reply(_("Bot will now kick for \002caps\002 (they must constitute at least\n" - "%d characters and %d%% of the entire message)."), ci->capsmin, ci->capspercent); + "%d characters and %d%% of the entire message)."), kd->capsmin, kd->capspercent); } else { - ci->Shrink("BS_KICK_CAPS"); + kd->caps = false; source.Reply(_("Bot won't kick for \002caps\002 anymore.")); } + + kd->Check(ci); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -335,7 +450,11 @@ class CommandBSKickColors : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_COLORS, "colors", "BS_KICK_COLORS"); + { + KickerData *kd = ci->Require<KickerData>("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_COLORS, "colors", kd, kd->colors); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -364,6 +483,8 @@ class CommandBSKickFlood : public CommandBSKickBase if (!CheckArguments(source, params, ci)) return; + KickerData *kd = ci->Require<KickerData>("kickerdata"); + if (params[1].equals_ci("ON")) { const Anope::string &ttb = params.size() > 2 ? params[2] : "", @@ -386,45 +507,47 @@ class CommandBSKickFlood : public CommandBSKickBase return; } - ci->ttb[TTB_FLOOD] = i; + kd->ttb[TTB_FLOOD] = i; } else - ci->ttb[TTB_FLOOD] = 0; + kd->ttb[TTB_FLOOD] = 0; - ci->floodlines = 6; + kd->floodlines = 6; try { - ci->floodlines = convertTo<int16_t>(lines); + kd->floodlines = convertTo<int16_t>(lines); } catch (const ConvertException &) { } - if (ci->floodlines < 2) - ci->floodlines = 6; + if (kd->floodlines < 2) + kd->floodlines = 6; - ci->floodsecs = 10; + kd->floodsecs = 10; try { - ci->floodsecs = convertTo<int16_t>(secs); + kd->floodsecs = convertTo<int16_t>(secs); } catch (const ConvertException &) { } - if (ci->floodsecs < 1) - ci->floodsecs = 10; - if (ci->floodsecs > Config->GetModule(me)->Get<time_t>("keepdata")) - ci->floodsecs = Config->GetModule(me)->Get<time_t>("keepdata"); + if (kd->floodsecs < 1) + kd->floodsecs = 10; + if (kd->floodsecs > Config->GetModule(me)->Get<time_t>("keepdata")) + kd->floodsecs = Config->GetModule(me)->Get<time_t>("keepdata"); - ci->ExtendMetadata("BS_KICK_FLOOD"); - if (ci->ttb[TTB_FLOOD]) + kd->flood = true; + if (kd->ttb[TTB_FLOOD]) source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds\n" - "and will place a ban after %d kicks for the same user."), ci->floodlines, ci->floodsecs, ci->ttb[TTB_FLOOD]); + "and will place a ban after %d kicks for the same user."), kd->floodlines, kd->floodsecs, kd->ttb[TTB_FLOOD]); else - source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds)."), ci->floodlines, ci->floodsecs); + source.Reply(_("Bot will now kick for \002flood\002 (%d lines in %d seconds)."), kd->floodlines, kd->floodsecs); } else if (params[1].equals_ci("OFF")) { - ci->Shrink("BS_KICK_FLOOD"); + kd->flood = false; source.Reply(_("Bot won't kick for \002flood\002 anymore.")); } else this->OnSyntaxError(source, params[1]); + + kd->Check(ci); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -454,7 +577,11 @@ class CommandBSKickItalics : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", "BS_KICK_ITALICS"); + { + KickerData *kd = ci->Require<KickerData>("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", kd, kd->italics); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -483,6 +610,8 @@ class CommandBSKickRepeat : public CommandBSKickBase if (!CheckArguments(source, params, ci)) return; + KickerData *kd = ci->Require<KickerData>("kickerdata"); + if (params[1].equals_ci("ON")) { const Anope::string &ttb = params[2], @@ -504,36 +633,38 @@ class CommandBSKickRepeat : public CommandBSKickBase return; } - ci->ttb[TTB_REPEAT] = i; + kd->ttb[TTB_REPEAT] = i; } else - ci->ttb[TTB_REPEAT] = 0; + kd->ttb[TTB_REPEAT] = 0; - ci->repeattimes = 3; + kd->repeattimes = 3; try { - ci->repeattimes = convertTo<int16_t>(times); + kd->repeattimes = convertTo<int16_t>(times); } catch (const ConvertException &) { } - if (ci->repeattimes < 2) - ci->repeattimes = 3; + if (kd->repeattimes < 2) + kd->repeattimes = 3; - ci->ExtendMetadata("BS_KICK_REPEAT"); - if (ci->ttb[TTB_REPEAT]) + kd->repeat = true; + if (kd->ttb[TTB_REPEAT]) source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n" "same thing %d times), and will place a ban after %d\n" - "kicks for the same user."), ci->repeattimes, ci->ttb[TTB_REPEAT]); + "kicks for the same user."), kd->repeattimes, kd->ttb[TTB_REPEAT]); else source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n" - "same thing %d times)."), ci->repeattimes); + "same thing %d times)."), kd->repeattimes); } else if (params[1].equals_ci("OFF")) { - ci->Shrink("BS_KICK_REPEAT"); + kd->repeat = false; source.Reply(_("Bot won't kick for \002repeats\002 anymore.")); } else this->OnSyntaxError(source, params[1]); + + kd->Check(ci); } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -562,7 +693,11 @@ class CommandBSKickReverses : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", "BS_KICK_ITALICS"); + { + KickerData *kd = ci->Require<KickerData>("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_REVERSES, "reverses", kd, kd->reverses); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -589,7 +724,11 @@ class CommandBSKickUnderlines : public CommandBSKickBase { ChannelInfo *ci; if (CheckArguments(source, params, ci)) - Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_ITALICS, "italics", "BS_KICK_ITALICS"); + { + KickerData *kd = ci->Require<KickerData>("kickerdata"); + Process(source, ci, params[1], params.size() > 2 ? params[2] : "", TTB_UNDERLINES, "underlines", kd, kd->underlines); + kd->Check(ci); + } } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -603,7 +742,137 @@ class CommandBSKickUnderlines : public CommandBSKickBase } }; -struct BanData : ExtensibleItem +class CommandBSSetDontKickOps : public Command +{ + public: + CommandBSSetDontKickOps(Module *creator, const Anope::string &sname = "botserv/set/dontkickops") : Command(creator, sname, 2, 2) + { + this->SetDesc(_("To protect ops against bot kicks")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + AccessGroup access = source.AccessFor(ci); + if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (Anope::ReadOnly) + { + source.Reply(_("Sorry, bot option setting is temporarily disabled.")); + return; + } + + KickerData *kd = ci->Require<KickerData>("kickerdata"); + if (params[1].equals_ci("ON")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickops"; + + kd->dontkickops = true; + source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickops"; + + kd->dontkickops = false; + source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, source.command); + + kd->Check(ci); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(_(" \n" + "Enables or disables \002ops protection\002 mode on a channel.\n" + "When it is enabled, ops won't be kicked by the bot\n" + "even if they don't match the NOKICK level.")); + return true; + } +}; + +class CommandBSSetDontKickVoices : public Command +{ + public: + CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname = "botserv/set/dontkickvoices") : Command(creator, sname, 2, 2) + { + this->SetDesc(_("To protect voices against bot kicks")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + AccessGroup access = source.AccessFor(ci); + if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (Anope::ReadOnly) + { + source.Reply(_("Sorry, bot option setting is temporarily disabled.")); + return; + } + + KickerData *kd = ci->Require<KickerData>("kickerdata"); + if (params[1].equals_ci("ON")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickvoices"; + + kd->dontkickvoices = true; + source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + bool override = !access.HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickvoices"; + + kd->dontkickvoices = false; + source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, source.command); + + kd->Check(ci); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(_(" \n" + "Enables or disables \002voices protection\002 mode on a channel.\n" + "When it is enabled, voices won't be kicked by the bot\n" + "even if they don't match the NOKICK level.")); + return true; + } +}; + +struct BanData { struct Data { @@ -613,11 +882,6 @@ struct BanData : ExtensibleItem Data() { - this->Clear(); - } - - void Clear() - { last_use = 0; for (int i = 0; i < TTB_SIZE; ++i) this->ttb[i] = 0; @@ -625,10 +889,12 @@ struct BanData : ExtensibleItem }; private: - typedef std::map<Anope::string, Data, ci::less> data_type; + typedef Anope::map<Data> data_type; data_type data_map; public: + BanData(Extensible *) { } + Data &get(const Anope::string &key) { return this->data_map[key]; @@ -654,14 +920,9 @@ struct BanData : ExtensibleItem } }; -struct UserData : ExtensibleItem +struct UserData { - UserData() - { - this->Clear(); - } - - void Clear() + UserData(Extensible *) { last_use = last_start = Anope::CurTime; lines = times = 0; @@ -682,7 +943,6 @@ struct UserData : ExtensibleItem Anope::string lastline; }; - class BanDataPurger : public Timer { public: @@ -696,12 +956,12 @@ class BanDataPurger : public Timer { Channel *c = it->second; - BanData *bd = c->GetExt<BanData *>("bs_main_bandata"); + BanData *bd = c->GetExt<BanData>("bandata"); if (bd != NULL) { bd->purge(); if (bd->empty()) - c->Shrink("bs_main_bandata"); + c->Shrink<BanData>("bandata"); } } } @@ -709,6 +969,10 @@ class BanDataPurger : public Timer class BSKick : public Module { + ExtensibleItem<BanData> bandata; + ExtensibleItem<UserData> userdata; + KickerDataImpl::ExtensibleItem kickerdata; + CommandBSKick commandbskick; CommandBSKickAMSG commandbskickamsg; CommandBSKickBadwords commandbskickbadwords; @@ -721,17 +985,14 @@ class BSKick : public Module CommandBSKickReverses commandbskickreverse; CommandBSKickUnderlines commandbskickunderlines; + CommandBSSetDontKickOps commandbssetdontkickops; + CommandBSSetDontKickVoices commandbssetdontkickvoices; + BanDataPurger purger; BanData::Data &GetBanData(User *u, Channel *c) { - BanData *bd = c->GetExt<BanData *>("bs_main_bandata"); - if (bd == NULL) - { - bd = new BanData(); - c->Extend("bs_main_bandata", bd); - } - + BanData *bd = bandata.Require(c); return bd->get(u->GetMask()); } @@ -741,17 +1002,11 @@ class BSKick : public Module if (uc == NULL) return NULL; - UserData *ud = uc->GetExt<UserData *>("bs_main_userdata"); - if (ud == NULL) - { - ud = new UserData(); - uc->Extend("bs_main_userdata", ud); - } - + UserData *ud = userdata.Require(uc); return ud; } - void check_ban(ChannelInfo *ci, User *u, int ttbtype) + void check_ban(ChannelInfo *ci, User *u, KickerData *kd, int ttbtype) { /* Don't ban ulines */ if (u->server->IsULined()) @@ -760,9 +1015,9 @@ class BSKick : public Module BanData::Data &bd = this->GetBanData(u, ci->c); ++bd.ttb[ttbtype]; - if (ci->ttb[ttbtype] && bd.ttb[ttbtype] >= ci->ttb[ttbtype]) + if (kd->ttb[ttbtype] && bd.ttb[ttbtype] >= kd->ttb[ttbtype]) { - /* Should not use == here because bd.ttb[ttbtype] could possibly be > ci->ttb[ttbtype] + /* Should not use == here because bd.ttb[ttbtype] could possibly be > kd->ttb[ttbtype] * if the TTB was changed after it was not set (0) before and the user had already been * kicked a few times. Bug #1056 - Adam */ @@ -793,26 +1048,136 @@ class BSKick : public Module public: BSKick(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), + bandata(this, "bandata"), + userdata(this, "userdata"), + kickerdata(this, "kickerdata"), + commandbskick(this), commandbskickamsg(this), commandbskickbadwords(this), commandbskickbolds(this), commandbskickcaps(this), commandbskickcolors(this), commandbskickflood(this), commandbskickitalics(this), commandbskickrepeat(this), commandbskickreverse(this), commandbskickunderlines(this), + commandbssetdontkickops(this), commandbssetdontkickvoices(this), + purger(this) { me = this; } - ~BSKick() + void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override { - for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) + if (!ci) + return; + + Anope::string enabled = Language::Translate(source.nc, _("Enabled")); + Anope::string disabled = Language::Translate(source.nc, _("Disabled")); + KickerData *kd = kickerdata.Get(ci); + + if (kd && kd->badwords) + { + if (kd->ttb[TTB_BADWORDS]) + info[_("Bad words kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BADWORDS]); + else + info[_("Bad words kicker")] = enabled; + } + else + info[_("Bad words kicker")] = disabled; + + if (kd && kd->bolds) + { + if (kd->ttb[TTB_BOLDS]) + info[_("Bolds kicker")] = Anope::printf("%s (%d kick(s) to ban)", enabled.c_str(), kd->ttb[TTB_BOLDS]); + else + info[_("Bolds kicker")] = enabled; + } + else + info[_("Bolds kicker")] = disabled; + + if (kd && kd->caps) + { + if (kd->ttb[TTB_CAPS]) + info[_("Caps kicker")] = Anope::printf(_("%s (%d kick(s) to ban; minimum %d/%d%%"), enabled.c_str(), kd->ttb[TTB_CAPS], kd->capsmin, kd->capspercent); + else + info[_("Caps kicker")] = Anope::printf(_("%s (minimum %d/%d%%)"), enabled.c_str(), kd->capsmin, kd->capspercent); + } + else + info[_("Caps kicker")] = disabled; + + if (kd && kd->colors) + { + if (kd->ttb[TTB_COLORS]) + info[_("Colors kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_COLORS]); + else + info[_("Colors kicker")] = enabled; + } + else + info[_("Colors kicker")] = disabled; + + if (kd && kd->flood) + { + if (kd->ttb[TTB_FLOOD]) + info[_("Flood kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d lines in %ds"), enabled.c_str(), kd->ttb[TTB_FLOOD], kd->floodlines, kd->floodsecs); + else + info[_("Flood kicker")] = Anope::printf(_("%s (%d lines in %ds)"), enabled.c_str(), kd->floodlines, kd->floodsecs); + } + else + info[_("Flood kicker")] = disabled; + + if (kd && kd->repeat) { - Channel *c = cit->second; - for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it) - it->second->Shrink("bs_main_userdata"); - c->Shrink("bs_main_bandata"); + if (kd->ttb[TTB_REPEAT]) + info[_("Repeat kicker")] = Anope::printf(_("%s (%d kick(s) to ban; %d times)"), enabled.c_str(), kd->ttb[TTB_REPEAT], kd->repeattimes); + else + info[_("Repeat kicker")] = Anope::printf(_("%s (%d times)"), enabled.c_str(), kd->repeattimes); + } + else + info[_("Repeat kicker")] = disabled; + + if (kd && kd->reverses) + { + if (kd->ttb[TTB_REVERSES]) + info[_("Reverses kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_REVERSES]); + else + info[_("Reverses kicker")] = enabled; } + else + info[_("Reverses kicker")] = disabled; + + if (kd && kd->underlines) + { + if (kd->ttb[TTB_UNDERLINES]) + info[_("Underlines kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_UNDERLINES]); + else + info[_("Underlines kicker")] = enabled; + } + else + info[_("Underlines kicker")] = disabled; + + if (kd && kd->italics) + { + if (kd->ttb[TTB_ITALICS]) + info[_("Italics kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_ITALICS]); + else + info[_("Italics kicker")] = enabled; + } + else + info[_("Italics kicker")] = disabled; + + if (kd && kd->amsgs) + { + if (kd->ttb[TTB_AMSGS]) + info[_("AMSG kicker")] = Anope::printf(_("%s (%d kick(s) to ban)"), enabled.c_str(), kd->ttb[TTB_AMSGS]); + else + info[_("AMSG kicker")] = enabled; + } + else + info[_("AMSG kicker")] = disabled; + + if (kd && kd->dontkickops) + info.AddOption(_("Ops Protection")); + if (kd && kd->dontkickvoices) + info.AddOption(_("Voices Protection")); } void OnPrivmsg(User *u, Channel *c, Anope::string &msg) anope_override @@ -828,12 +1193,15 @@ class BSKick : public Module ChannelInfo *ci = c->ci; if (ci == NULL) return; + KickerData *kd = kickerdata.Get(ci); + if (kd == NULL) + return; if (ci->AccessFor(u).HasPriv("NOKICK")) return; - else if (ci->HasExt("BS_DONTKICKOPS") && (c->HasUserStatus(u, "HALFOP") || c->HasUserStatus(u, "OP") || c->HasUserStatus(u, "PROTECT") || c->HasUserStatus(u, "OWNER"))) + else if (kd->dontkickops && (c->HasUserStatus(u, "HALFOP") || c->HasUserStatus(u, "OP") || c->HasUserStatus(u, "PROTECT") || c->HasUserStatus(u, "OWNER"))) return; - else if (ci->HasExt("BS_DONTKICKVOICES") && c->HasUserStatus(u, "VOICE")) + else if (kd->dontkickvoices && c->HasUserStatus(u, "VOICE")) return; Anope::string realbuf = msg; @@ -851,47 +1219,47 @@ class BSKick : public Module return; /* Bolds kicker */ - if (ci->HasExt("BS_KICK_BOLDS") && realbuf.find(2) != Anope::string::npos) + if (kd->bolds && realbuf.find(2) != Anope::string::npos) { - check_ban(ci, u, TTB_BOLDS); + check_ban(ci, u, kd, TTB_BOLDS); bot_kick(ci, u, _("Don't use bolds on this channel!")); return; } /* Color kicker */ - if (ci->HasExt("BS_KICK_COLORS") && realbuf.find(3) != Anope::string::npos) + if (kd->colors && realbuf.find(3) != Anope::string::npos) { - check_ban(ci, u, TTB_COLORS); + check_ban(ci, u, kd, TTB_COLORS); bot_kick(ci, u, _("Don't use colors on this channel!")); return; } /* Reverses kicker */ - if (ci->HasExt("BS_KICK_REVERSES") && realbuf.find(22) != Anope::string::npos) + if (kd->reverses && realbuf.find(22) != Anope::string::npos) { - check_ban(ci, u, TTB_REVERSES); + check_ban(ci, u, kd, TTB_REVERSES); bot_kick(ci, u, _("Don't use reverses on this channel!")); return; } /* Italics kicker */ - if (ci->HasExt("BS_KICK_ITALICS") && realbuf.find(29) != Anope::string::npos) + if (kd->italics && realbuf.find(29) != Anope::string::npos) { - check_ban(ci, u, TTB_ITALICS); + check_ban(ci, u, kd, TTB_ITALICS); bot_kick(ci, u, _("Don't use italics on this channel!")); return; } /* Underlines kicker */ - if (ci->HasExt("BS_KICK_UNDERLINES") && realbuf.find(31) != Anope::string::npos) + if (kd->underlines && realbuf.find(31) != Anope::string::npos) { - check_ban(ci, u, TTB_UNDERLINES); + check_ban(ci, u, kd, TTB_UNDERLINES); bot_kick(ci, u, _("Don't use underlines on this channel!")); return; } /* Caps kicker */ - if (ci->HasExt("BS_KICK_CAPS") && realbuf.length() >= static_cast<unsigned>(ci->capsmin)) + if (kd->caps && realbuf.length() >= static_cast<unsigned>(kd->capsmin)) { int i = 0, l = 0; @@ -908,26 +1276,27 @@ class BSKick : public Module * percentage of caps to kick for; the rest is ignored. -GD */ - if ((i || l) && i >= ci->capsmin && i * 100 / (i + l) >= ci->capspercent) + if ((i || l) && i >= kd->capsmin && i * 100 / (i + l) >= kd->capspercent) { - check_ban(ci, u, TTB_CAPS); + check_ban(ci, u, kd, TTB_CAPS); bot_kick(ci, u, _("Turn caps lock OFF!")); return; } } /* Bad words kicker */ - if (ci->HasExt("BS_KICK_BADWORDS")) + if (kd->badwords) { bool mustkick = false; + BadWords *badwords = ci->GetExt<BadWords>("badwords"); /* Normalize the buffer */ Anope::string nbuf = Anope::NormalizeBuffer(realbuf); bool casesensitive = Config->GetModule("botserv")->Get<bool>("casesensitive"); - for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i) + for (unsigned i = 0; badwords && i < badwords->GetBadWordCount(); ++i) { - const BadWord *bw = ci->GetBadWord(i); + const BadWord *bw = badwords->GetBadWord(i); if (bw->type == BW_ANY && ((casesensitive && nbuf.find(bw->word) != Anope::string::npos) || (!casesensitive && nbuf.find_ci(bw->word) != Anope::string::npos))) mustkick = true; @@ -983,7 +1352,7 @@ class BSKick : public Module if (mustkick) { - check_ban(ci, u, TTB_BADWORDS); + check_ban(ci, u, kd, TTB_BADWORDS); if (Config->GetModule(me)->Get<bool>("gentlebadwordreason")) bot_kick(ci, u, _("Watch your language!")); else @@ -999,34 +1368,34 @@ class BSKick : public Module if (ud) { /* Flood kicker */ - if (ci->HasExt("BS_KICK_FLOOD")) + if (kd->flood) { - if (Anope::CurTime - ud->last_start > ci->floodsecs) + if (Anope::CurTime - ud->last_start > kd->floodsecs) { ud->last_start = Anope::CurTime; ud->lines = 0; } ++ud->lines; - if (ud->lines >= ci->floodlines) + if (ud->lines >= kd->floodlines) { - check_ban(ci, u, TTB_FLOOD); + check_ban(ci, u, kd, TTB_FLOOD); bot_kick(ci, u, _("Stop flooding!")); return; } } /* Repeat kicker */ - if (ci->HasExt("BS_KICK_REPEAT")) + if (kd->repeat) { if (!ud->lastline.equals_ci(realbuf)) ud->times = 0; else ++ud->times; - if (ud->times >= ci->repeattimes) + if (ud->times >= kd->repeattimes) { - check_ban(ci, u, TTB_REPEAT); + check_ban(ci, u, kd, TTB_REPEAT); bot_kick(ci, u, _("Stop repeating yourself!")); return; } @@ -1039,9 +1408,9 @@ class BSKick : public Module Channel *chan = it->second->chan; ++it; - if (chan->ci && chan->ci->HasExt("BS_KICK_AMSGS") && !chan->ci->AccessFor(u).HasPriv("NOKICK")) + if (chan->ci && kd->amsgs && !chan->ci->AccessFor(u).HasPriv("NOKICK")) { - check_ban(chan->ci, u, TTB_AMSGS); + check_ban(chan->ci, u, kd, TTB_AMSGS); bot_kick(chan->ci, u, _("Don't use AMSGs!")); } } diff --git a/modules/commands/bs_set.cpp b/modules/commands/bs_set.cpp index fa2bd716f..4e1e77ad1 100644 --- a/modules/commands/bs_set.cpp +++ b/modules/commands/bs_set.cpp @@ -127,314 +127,6 @@ class CommandBSSetBanExpire : public Command } }; -class CommandBSSetDontKickOps : public Command -{ - public: - CommandBSSetDontKickOps(Module *creator, const Anope::string &sname = "botserv/set/dontkickops") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("To protect ops against bot kicks")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - AccessGroup access = source.AccessFor(ci); - if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (params[1].equals_ci("ON")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickops"; - - ci->ExtendMetadata("BS_DONTKICKOPS"); - source.Reply(_("Bot \002won't kick ops\002 on channel %s."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickops"; - - ci->Shrink("BS_DONTKICKOPS"); - source.Reply(_("Bot \002will kick ops\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002ops protection\002 mode on a channel.\n" - "When it is enabled, ops won't be kicked by the bot\n" - "even if they don't match the NOKICK level.")); - return true; - } -}; - -class CommandBSSetDontKickVoices : public Command -{ - public: - CommandBSSetDontKickVoices(Module *creator, const Anope::string &sname = "botserv/set/dontkickvoices") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("To protect voices against bot kicks")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - AccessGroup access = source.AccessFor(ci); - if (!source.HasPriv("botserv/administration") && !access.HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (params[1].equals_ci("ON")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable dontkickvoices"; - - ci->ExtendMetadata("BS_DONTKICKVOICES"); - source.Reply(_("Bot \002won't kick voices\002 on channel %s."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - bool override = !access.HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable dontkickvoices"; - - ci->Shrink("BS_DONTKICKVOICES"); - source.Reply(_("Bot \002will kick voices\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002voices protection\002 mode on a channel.\n" - "When it is enabled, voices won't be kicked by the bot\n" - "even if they don't match the NOKICK level.")); - return true; - } -}; - -class CommandBSSetFantasy : public Command -{ - public: - CommandBSSetFantasy(Module *creator, const Anope::string &sname = "botserv/set/fantasy") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("Enable fantaisist commands")); - this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - const Anope::string &value = params[1]; - - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (value.equals_ci("ON")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable fantasy"; - - ci->ExtendMetadata("BS_FANTASY"); - source.Reply(_("Fantasy mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable fantasy"; - - ci->Shrink("BS_FANTASY"); - source.Reply(_("Fantasy mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002fantasy\002 mode on a channel.\n" - "When it is enabled, users will be able to use\n" - "fantasy commands on a channel when prefixed\n" - "with one of the following fantasy characters: \002%s\002\n" - " \n" - "Note that users wanting to use fantaisist\n" - "commands MUST have enough access for both\n" - "the FANTASIA and the command they are executing."), - Config->GetModule("botserv")->Get<const Anope::string>("fantasycharacter", "!").c_str()); - return true; - } -}; - -class CommandBSSetGreet : public Command -{ - public: - CommandBSSetGreet(Module *creator, const Anope::string &sname = "botserv/set/greet") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("Enable greet messages")); - this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - const Anope::string &value = params[1]; - - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (Anope::ReadOnly) - { - source.Reply(_("Sorry, bot option setting is temporarily disabled.")); - return; - } - - if (value.equals_ci("ON")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable greets"; - - ci->ExtendMetadata("BS_GREET"); - source.Reply(_("Greet mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - bool override = !source.AccessFor(ci).HasPriv("SET"); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable greets"; - - ci->Shrink("BS_GREET"); - source.Reply(_("Greet mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "Enables or disables \002greet\002 mode on a channel.\n" - "When it is enabled, the bot will display greet\n" - "messages of users joining the channel, provided\n" - "they have enough access to the channel.")); - return true; - } -}; - -class CommandBSSetNoBot : public Command -{ - public: - CommandBSSetNoBot(Module *creator, const Anope::string &sname = "botserv/set/nobot") : Command(creator, sname, 2, 2) - { - this->SetDesc(_("Prevent a bot from being assigned to a channel")); - this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - const Anope::string &value = params[1]; - - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - if (value.equals_ci("ON")) - { - Log(LOG_ADMIN, source, this, ci) << "to enable nobot"; - - ci->ExtendMetadata("BS_NOBOT"); - if (ci->bi) - ci->bi->UnAssign(source.GetUser(), ci); - source.Reply(_("No-bot mode is now \002on\002 on channel %s."), ci->name.c_str()); - } - else if (value.equals_ci("OFF")) - { - Log(LOG_ADMIN, source, this, ci) << "to disable nobot"; - - ci->Shrink("BS_NOBOT"); - source.Reply(_("No-bot mode is now \002off\002 on channel %s."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, source.command); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(_(" \n" - "This option makes a channel be unassignable. If a bot\n" - "is already assigned to the channel, it is unassigned\n" - "automatically when you enable the option.")); - return true; - } -}; - class CommandBSSetPrivate : public Command { public: @@ -483,17 +175,12 @@ class BSSet : public Module { CommandBSSet commandbsset; CommandBSSetBanExpire commandbssetbanexpire; - CommandBSSetDontKickOps commandbssetdontkickops; - CommandBSSetDontKickVoices commandbssetdontkickvoices; - CommandBSSetFantasy commandbssetfantasy; - CommandBSSetGreet commandbssetgreet; - CommandBSSetNoBot commandbssetnobot; CommandBSSetPrivate commandbssetprivate; public: BSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandbsset(this), commandbssetbanexpire(this), commandbssetdontkickops(this), commandbssetdontkickvoices(this), - commandbssetfantasy(this), commandbssetgreet(this), commandbssetnobot(this), commandbssetprivate(this) + commandbsset(this), commandbssetbanexpire(this), + commandbssetprivate(this) { } diff --git a/modules/commands/cs_akick.cpp b/modules/commands/cs_akick.cpp index 17a20a0dd..3764c54fc 100644 --- a/modules/commands/cs_akick.cpp +++ b/modules/commands/cs_akick.cpp @@ -20,8 +20,8 @@ class CommandCSAKick : public Command const NickAlias *na = NickAlias::Find(mask); NickCore *nc = NULL; const AutoKick *akick; - unsigned reasonmax = Config->GetModule("chanserv")->Get<unsigned>("reasonmax", "200"); + if (reason.length() > reasonmax) reason = reason.substr(0, reasonmax); diff --git a/modules/commands/cs_clone.cpp b/modules/commands/cs_clone.cpp index d9ba97894..a1b04d103 100644 --- a/modules/commands/cs_clone.cpp +++ b/modules/commands/cs_clone.cpp @@ -10,6 +10,7 @@ */ #include "module.h" +#include "modules/bs_badwords.h" class CommandCSClone : public Command { @@ -125,12 +126,16 @@ public: } else if (what.equals_ci("BADWORDS")) { - target_ci->ClearBadWords(); - for (unsigned i = 0; i < ci->GetBadWordCount(); ++i) - { - const BadWord *bw = ci->GetBadWord(i); - target_ci->AddBadWord(bw->word, bw->type); - } + BadWords *target_badwords = target_ci->GetExt<BadWords>("badwords"), + *badwords = ci->Require<BadWords>("badwords"); + if (target_badwords) + target_badwords->ClearBadWords(); + if (badwords) + for (unsigned i = 0; i < badwords->GetBadWordCount(); ++i) + { + const BadWord *bw = badwords->GetBadWord(i); + target_badwords->AddBadWord(bw->word, bw->type); + } source.Reply(_("All badword entries from \002%s\002 have been cloned to \002%s\002."), channel.c_str(), target.c_str()); } @@ -141,8 +146,6 @@ public: } Log(LOG_COMMAND, source, this, ci) << "to clone " << (what.empty() ? "everything from it" : what) << " to " << target_ci->name; - - return; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override diff --git a/modules/commands/cs_drop.cpp b/modules/commands/cs_drop.cpp index 3f14aea65..adbe1f2ca 100644 --- a/modules/commands/cs_drop.cpp +++ b/modules/commands/cs_drop.cpp @@ -37,23 +37,20 @@ class CommandCSDrop : public Command return; } - if (ci->HasExt("SUSPENDED") && !source.HasCommand("chanserv/drop")) - { - source.Reply(CHAN_X_SUSPENDED, chan.c_str()); - return; - } - if ((ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && !source.HasCommand("chanserv/drop")) { source.Reply(ACCESS_DENIED); return; } + EventReturn MOD_RESULT; + FOREACH_RESULT(OnChanDrop, MOD_RESULT, (source, ci)); + if (MOD_RESULT == EVENT_STOP) + return; + bool override = (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "(founder was: " << (ci->GetFounder() ? ci->GetFounder()->display : "none") << ")"; - FOREACH_MOD(OnChanDrop, (ci)); - Reference<Channel> c = ci->c; delete ci; diff --git a/modules/commands/cs_enforce.cpp b/modules/commands/cs_enforce.cpp index e40676186..b683332f5 100644 --- a/modules/commands/cs_enforce.cpp +++ b/modules/commands/cs_enforce.cpp @@ -27,7 +27,7 @@ class CommandCSEnforce : public Command * if it's off. */ bool hadsecureops = ci->HasExt("SECUREOPS"); - ci->ExtendMetadata("SECUREOPS"); + ci->Extend<bool>("SECUREOPS"); for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it) { @@ -37,7 +37,7 @@ class CommandCSEnforce : public Command } if (!hadsecureops) - ci->Shrink("SECUREOPS"); + ci->Shrink<bool>("SECUREOPS"); source.Reply(_("Secureops enforced on %s."), ci->name.c_str()); } diff --git a/modules/commands/cs_entrymsg.cpp b/modules/commands/cs_entrymsg.cpp index f94aca44c..d3d3bb71b 100644 --- a/modules/commands/cs_entrymsg.cpp +++ b/modules/commands/cs_entrymsg.cpp @@ -37,9 +37,9 @@ struct EntryMsg : Serializable static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); }; -struct EntryMessageList : Serialize::Checker<std::vector<EntryMsg *> >, ExtensibleItem +struct EntryMessageList : Serialize::Checker<std::vector<EntryMsg *> > { - EntryMessageList() : Serialize::Checker<std::vector<EntryMsg *> >("EntryMsg") { } + EntryMessageList(Extensible *) : Serialize::Checker<std::vector<EntryMsg *> >("EntryMsg") { } ~EntryMessageList() { @@ -71,12 +71,9 @@ Serializable* EntryMsg::Unserialize(Serializable *obj, Serialize::Data &data) return msg; } - EntryMessageList *messages = ci->GetExt<EntryMessageList *>("cs_entrymsg"); + EntryMessageList *messages = ci->GetExt<EntryMessageList>("entrymsg"); if (messages == NULL) - { - messages = new EntryMessageList(); - ci->Extend("cs_entrymsg", messages); - } + messages = ci->Extend<EntryMessageList>("entrymsg"); data["when"] >> swhen; @@ -90,12 +87,9 @@ class CommandEntryMessage : public Command private: void DoList(CommandSource &source, ChannelInfo *ci) { - EntryMessageList *messages = ci->GetExt<EntryMessageList *>("cs_entrymsg"); + EntryMessageList *messages = ci->GetExt<EntryMessageList>("entrymsg"); if (messages == NULL) - { - messages = new EntryMessageList(); - ci->Extend("cs_entrymsg", messages); - } + messages = ci->Extend<EntryMessageList>("entrymsg"); if ((*messages)->empty()) { @@ -129,12 +123,9 @@ class CommandEntryMessage : public Command void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &message) { - EntryMessageList *messages = ci->GetExt<EntryMessageList *>("cs_entrymsg"); + EntryMessageList *messages = ci->GetExt<EntryMessageList>("entrymsg"); if (messages == NULL) - { - messages = new EntryMessageList(); - ci->Extend("cs_entrymsg", messages); - } + messages = ci->Extend<EntryMessageList>("entrymsg"); if ((*messages)->size() >= Config->GetModule(this->owner)->Get<unsigned>("maxentries")) source.Reply(_("The entry message list for \002%s\002 is full."), ci->name.c_str()); @@ -148,12 +139,9 @@ class CommandEntryMessage : public Command void DoDel(CommandSource &source, ChannelInfo *ci, const Anope::string &message) { - EntryMessageList *messages = ci->GetExt<EntryMessageList *>("cs_entrymsg"); + EntryMessageList *messages = ci->GetExt<EntryMessageList>("entrymsg"); if (messages == NULL) - { - messages = new EntryMessageList(); - ci->Extend("cs_entrymsg", messages); - } + messages = ci->Extend<EntryMessageList>("entrymsg"); if (!message.is_pos_number_only()) source.Reply(("Entry message \002%s\002 not found on channel \002%s\002."), message.c_str(), ci->name.c_str()); @@ -169,7 +157,7 @@ class CommandEntryMessage : public Command delete (*messages)->at(i - 1); (*messages)->erase((*messages)->begin() + i - 1); if ((*messages)->empty()) - ci->Shrink("cs_entrymsg"); + ci->Shrink<EntryMessageList>("entrymsg"); Log(source.IsFounder(ci) ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove a message"; source.Reply(_("Entry message \002%i\002 for \002%s\002 deleted."), i, ci->name.c_str()); } @@ -185,14 +173,7 @@ class CommandEntryMessage : public Command void DoClear(CommandSource &source, ChannelInfo *ci) { - EntryMessageList *messages = ci->GetExt<EntryMessageList *>("cs_entrymsg"); - if (messages != NULL) - { - for (unsigned i = 0; i < (*messages)->size(); ++i) - delete (*messages)->at(i); - (*messages)->clear(); - ci->Shrink("cs_entrymsg"); - } + ci->Shrink<EntryMessageList>("entrymsg"); Log(source.IsFounder(ci) ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to remove all messages"; source.Reply(_("Entry messages for \002%s\002 have been cleared."), ci->name.c_str()); @@ -267,9 +248,10 @@ class CSEntryMessage : public Module { Serialize::Type entrymsg_type; CommandEntryMessage commandentrymsg; + ExtensibleItem<EntryMessageList> eml; public: - CSEntryMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), entrymsg_type("EntryMsg", EntryMsg::Unserialize), commandentrymsg(this) + CSEntryMessage(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), entrymsg_type("EntryMsg", EntryMsg::Unserialize), commandentrymsg(this), eml(this, "entrymsg") { } @@ -278,7 +260,7 @@ class CSEntryMessage : public Module { if (u && c && c->ci && u->server->IsSynced()) { - EntryMessageList *messages = c->ci->GetExt<EntryMessageList *>("cs_entrymsg"); + EntryMessageList *messages = c->ci->GetExt<EntryMessageList>("entrymsg"); if (messages != NULL) for (unsigned i = 0; i < (*messages)->size(); ++i) diff --git a/modules/commands/cs_info.cpp b/modules/commands/cs_info.cpp index 0d3623c00..8f910fc83 100644 --- a/modules/commands/cs_info.cpp +++ b/modules/commands/cs_info.cpp @@ -13,17 +13,6 @@ class CommandCSInfo : public Command { - void CheckOptStr(Anope::string &buf, const Anope::string &opt, const char *str, const ChannelInfo *ci, const NickCore *nc) - { - if (ci->HasExt(opt)) - { - if (!buf.empty()) - buf += ", "; - - buf += Language::Translate(nc, str); - } - } - public: CommandCSInfo(Module *creator) : Command(creator, "chanserv/info", 1, 2) { @@ -66,43 +55,9 @@ class CommandCSInfo : public Command info["Registered"] = Anope::strftime(ci->time_registered); info["Last used"] = Anope::strftime(ci->last_used); - const ModeLock *secret = ci->GetMLock("SECRET"); - if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode("SECRET"))))) - { - info["Last topic"] = ci->last_topic; - info["Topic set by"] = ci->last_topic_setter; - } - if (show_all) { info["Ban type"] = stringify(ci->bantype); - - Anope::string optbuf; - CheckOptStr(optbuf, "KEEPTOPIC", _("Topic Retention"), ci, nc); - CheckOptStr(optbuf, "PEACE", _("Peace"), ci, nc); - CheckOptStr(optbuf, "PRIVATE", _("Private"), ci, nc); - CheckOptStr(optbuf, "RESTRICTED", _("Restricted Access"), ci, nc); - CheckOptStr(optbuf, "SECURE", _("Secure"), ci, nc); - CheckOptStr(optbuf, "SECUREFOUNDER", _("Secure Founder"), ci, nc); - CheckOptStr(optbuf, "SECUREOPS", _("Secure Ops"), ci, nc); - if (ci->HasExt("SIGNKICK")) - CheckOptStr(optbuf, "SIGNKICK", _("Signed kicks"), ci, nc); - else - CheckOptStr(optbuf, "SIGNKICK_LEVEL", _("Signed kicks"), ci, nc); - CheckOptStr(optbuf, "TOPICLOCK", _("Topic Lock"), ci, nc); - CheckOptStr(optbuf, "PERSIST", _("Persistent"), ci, nc); - CheckOptStr(optbuf, "NO_EXPIRE", _("No expire"), ci, nc); - CheckOptStr(optbuf, "STATS", _("Chanstats"), ci, nc); - - info["Options"] = optbuf.empty() ? _("None") : optbuf; - - const Anope::string &ml = ci->GetMLockAsString(true); - if (!ml.empty()) - info["Mode lock"] = ml; - - time_t chanserv_expire = Config->GetModule("chanserv")->Get<time_t>("expire", "14d"); - if (!ci->HasExt("NO_EXPIRE") && chanserv_expire && !Anope::NoExpire) - info["Expires on"] = Anope::strftime(ci->last_used + chanserv_expire); } FOREACH_MOD(OnChanInfo, (source, ci, info, show_all)); diff --git a/modules/commands/cs_list.cpp b/modules/commands/cs_list.cpp index 9e2df0a62..a92729b61 100644 --- a/modules/commands/cs_list.cpp +++ b/modules/commands/cs_list.cpp @@ -81,11 +81,11 @@ class CommandCSList : public Command { const ChannelInfo *ci = it->second; - if (!is_servadmin && (ci->HasExt("PRIVATE") || ci->HasExt("SUSPENDED"))) + if (!is_servadmin && (ci->HasExt("CS_PRIVATE") || ci->HasExt("SUSPENDED"))) continue; else if (suspended && !ci->HasExt("SUSPENDED")) continue; - else if (channoexpire && !ci->HasExt("NO_EXPIRE")) + else if (channoexpire && !ci->HasExt("CS_NO_EXPIRE")) continue; if (pattern.equals_ci(ci->name) || ci->name.equals_ci(spattern) || Anope::Match(ci->name, pattern, false, true) || Anope::Match(ci->name, spattern, false, true)) @@ -93,7 +93,7 @@ class CommandCSList : public Command if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nchans <= listmax) { bool isnoexpire = false; - if (is_servadmin && (ci->HasExt("NO_EXPIRE"))) + if (is_servadmin && (ci->HasExt("CS_NO_EXPIRE"))) isnoexpire = true; ListFormatter::ListEntry entry; @@ -159,14 +159,89 @@ class CommandCSList : public Command } }; +class CommandCSSetPrivate : public Command +{ + public: + CommandCSSetPrivate(Module *creator, const Anope::string &cname = "chanserv/set/private") : Command(creator, cname, 2, 2) + { + this->SetDesc(_("Hide channel from the LIST command")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1])); + if (MOD_RESULT == EVENT_STOP) + return; + + if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable private"; + ci->Extend<bool>("CS_PRIVATE"); + source.Reply(_("Private option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable private"; + ci->Shrink<bool>("CS_PRIVATE"); + source.Reply(_("Private option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "PRIVATE"); + + return; + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables the \002private\002 option for a channel.")); + + BotInfo *bi; + Anope::string cmd; + if (Command::FindCommandFromService("chanserv/list", bi, cmd)) + source.Reply(_("When \002private\002 is set, the channel will not appear in\n" + "%s's %s command."), bi->nick.c_str(), cmd.c_str()); + return true; + } +}; + class CSList : public Module { CommandCSList commandcslist; + CommandCSSetPrivate commandcssetprivate; + + SerializableExtensibleItem<bool> priv; public: - CSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandcslist(this) + CSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), + commandcslist(this), commandcssetprivate(this), priv(this, "CS_PRIVATE") { } + + void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override + { + if (!show_all) + return; + + if (priv.HasExt(ci)) + info.AddOption(_("Private")); + } }; MODULE_INIT(CSList) diff --git a/modules/commands/cs_log.cpp b/modules/commands/cs_log.cpp index 969ebe5ab..bc6ba73c6 100644 --- a/modules/commands/cs_log.cpp +++ b/modules/commands/cs_log.cpp @@ -10,6 +10,77 @@ */ #include "module.h" +#include "modules/cs_log.h" + +struct LogSettingImpl : LogSetting, Serializable +{ + LogSettingImpl() : Serializable("LogSetting") + { + } + + ~LogSettingImpl() + { + ChannelInfo *ci = ChannelInfo::Find(chan); + if (ci) + { + LogSettings *ls = ci->Require<LogSettings>("logsettings"); + LogSettings::iterator it = std::find((*ls)->begin(), (*ls)->end(), this); + if (it != (*ls)->end()) + (*ls)->erase(it); + } + } + + void Serialize(Serialize::Data &data) const anope_override + { + data["ci"] << chan; + data["service_name"] << service_name; + data["command_service"] << command_service; + data["command_name"] << command_name; + data["method"] << method; + data["extra"] << extra; + data["creator"] << creator; + data.SetType("created", Serialize::Data::DT_INT); data["created"] << created; + } + + static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) + { + Anope::string sci; + data["ci"] >> sci; + + ChannelInfo *ci = ChannelInfo::Find(sci); + if (ci == NULL) + return NULL; + + LogSetting *ls; + if (obj) + ls = anope_dynamic_static_cast<LogSettingImpl *>(obj); + else + { + LogSettings *lsettings = ci->Require<LogSettings>("logsettings"); + ls = new LogSettingImpl(); + (*lsettings)->push_back(ls); + } + + ls->chan = ci->name; + data["service_name"] >> ls->service_name; + data["command_service"] >> ls->command_service; + data["command_name"] >> ls->command_name; + data["method"] >> ls->method; + data["extra"] >> ls->extra; + data["creator"] >> ls->creator; + data["created"] >> ls->created; + } +}; + +struct LogSettingsImpl : LogSettings +{ + LogSettingsImpl(Extensible *) { } + + LogSetting *Create() anope_override + { + return new LogSettingImpl(); + } +}; class CommandCSLog : public Command { @@ -32,16 +103,17 @@ public: source.Reply(ACCESS_DENIED); else if (params.size() == 1) { - if (ci->log_settings->empty()) + LogSettings *ls = ci->Require<LogSettings>("logsettings"); + if (!ls || (*ls)->empty()) source.Reply(_("There currently are no logging configurations for %s."), ci->name.c_str()); else { ListFormatter list; list.AddColumn("Number").AddColumn("Service").AddColumn("Command").AddColumn("Method").AddColumn(""); - for (unsigned i = 0; i < ci->log_settings->size(); ++i) + for (unsigned i = 0; i < (*ls)->size(); ++i) { - const LogSetting *log = ci->log_settings->at(i); + const LogSetting *log = (*ls)->at(i); ListFormatter::ListEntry entry; entry["Number"] = stringify(i + 1); @@ -63,6 +135,7 @@ public: } else if (params.size() > 2) { + LogSettings *ls = ci->Require<LogSettings>("logsettings"); const Anope::string &command = params[1]; const Anope::string &method = params[2]; const Anope::string &extra = params.size() > 3 ? params[3] : ""; @@ -106,16 +179,15 @@ public: bool override = !source.AccessFor(ci).HasPriv("SET"); - for (unsigned i = ci->log_settings->size(); i > 0; --i) + for (unsigned i = (*ls)->size(); i > 0; --i) { - LogSetting *log = ci->log_settings->at(i - 1); + LogSetting *log = (*ls)->at(i - 1); if (log->service_name == bi->commands[command_name].name && log->method.equals_ci(method)) { if (log->extra == extra) { delete log; - ci->log_settings->erase(ci->log_settings->begin() + i - 1); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to remove logging for " << command << " with method " << method << (extra == "" ? "" : " ") << extra; source.Reply(_("Logging for command %s on %s with log method %s%s%s has been removed."), command_name.c_str(), bi->nick.c_str(), method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str()); } @@ -129,8 +201,8 @@ public: } } - LogSetting *log = new LogSetting(); - log->ci = ci; + LogSetting *log = new LogSettingImpl(); + log->chan = ci->name; log->service_name = bi->commands[command_name].name; log->command_service = bi->nick; log->command_name = command_name; @@ -139,7 +211,8 @@ public: log->created = Anope::CurTime; log->creator = source.GetNick(); - ci->log_settings->push_back(log); + (*ls)->push_back(log); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to log " << command << " with method " << method << (extra == "" ? "" : " ") << extra; source.Reply(_("Logging is now active for command %s on %s, using log method %s%s%s."), command_name.c_str(), bi->nick.c_str(), method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str()); @@ -180,10 +253,13 @@ class CSLog : public Module { ServiceReference<MemoServService> MSService; CommandCSLog commandcslog; + ExtensibleItem<LogSettingsImpl> logsettings; + Serialize::Type logsetting_type; public: CSLog(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - MSService("MemoServService", "MemoServ"), commandcslog(this) + MSService("MemoServService", "MemoServ"), commandcslog(this), logsettings(this, "logsettings"), + logsetting_type("LogSetting", LogSettingImpl::Unserialize) { } @@ -193,25 +269,27 @@ class CSLog : public Module if (l->type != LOG_COMMAND || l->u == NULL || l->c == NULL || l->ci == NULL || !Me || !Me->IsSynced()) return; - for (unsigned i = l->ci->log_settings->size(); i > 0; --i) - { - const LogSetting *log = l->ci->log_settings->at(i - 1); - - if (log->service_name == l->c->name) + LogSettings *ls = logsettings.Get(l->ci); + if (ls) + for (unsigned i = 0; i < (*ls)->size(); ++i) { - Anope::string buffer = l->u->nick + " used " + log->command_name + " " + l->buf.str(); + const LogSetting *log = (*ls)->at(i); - if (log->method.equals_ci("MESSAGE") && l->ci->c && l->ci->bi && l->ci->c->FindUser(l->ci->bi) != NULL) + if (log->service_name == l->c->name) { - IRCD->SendPrivmsg(l->ci->bi, log->extra + l->ci->c->name, "%s", buffer.c_str()); - l->ci->bi->lastmsg = Anope::CurTime; + Anope::string buffer = l->u->nick + " used " + log->command_name + " " + l->buf.str(); + + if (log->method.equals_ci("MESSAGE") && l->ci->c && l->ci->bi && l->ci->c->FindUser(l->ci->bi) != NULL) + { + IRCD->SendPrivmsg(l->ci->bi, log->extra + l->ci->c->name, "%s", buffer.c_str()); + l->ci->bi->lastmsg = Anope::CurTime; + } + else if (log->method.equals_ci("NOTICE") && l->ci->c && l->ci->bi && l->ci->c->FindUser(l->ci->bi) != NULL) + IRCD->SendNotice(l->ci->bi, log->extra + l->ci->c->name, "%s", buffer.c_str()); + else if (log->method.equals_ci("MEMO") && MSService && l->ci->WhoSends() != NULL) + MSService->Send(l->ci->WhoSends()->nick, l->ci->name, buffer, true); } - else if (log->method.equals_ci("NOTICE") && l->ci->c && l->ci->bi && l->ci->c->FindUser(l->ci->bi) != NULL) - IRCD->SendNotice(l->ci->bi, log->extra + l->ci->c->name, "%s", buffer.c_str()); - else if (log->method.equals_ci("MEMO") && MSService && l->ci->WhoSends() != NULL) - MSService->Send(l->ci->WhoSends()->nick, l->ci->name, buffer, true); } - } } }; diff --git a/modules/commands/cs_mode.cpp b/modules/commands/cs_mode.cpp index 895bd3d5b..1299bc543 100644 --- a/modules/commands/cs_mode.cpp +++ b/modules/commands/cs_mode.cpp @@ -10,6 +10,234 @@ */ #include "module.h" +#include "modules/cs_mode.h" + +struct ModeLockImpl : ModeLock, Serializable +{ + ModeLockImpl() : Serializable("ModeLock") + { + } + + ~ModeLockImpl() + { + ChannelInfo *chan = ChannelInfo::Find(ci); + if (chan) + { + ModeLocks *ml = chan->GetExt<ModeLocks>("modelocks"); + if (ml) + ml->RemoveMLock(this); + } + } + + void Serialize(Serialize::Data &data) const anope_override; + static Serializable* Unserialize(Serializable *obj, Serialize::Data &data); +}; + +struct ModeLocksImpl : ModeLocks +{ + Serialize::Reference<ChannelInfo> ci; + Serialize::Checker<ModeList> mlocks; + + ModeLocksImpl(Extensible *obj) : ci(anope_dynamic_static_cast<ChannelInfo *>(obj)), mlocks("ModeLock") + { + } + + bool HasMLock(ChannelMode *mode, const Anope::string ¶m, bool status) const anope_override + { + if (!mode) + return false; + + for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) + { + const ModeLock *ml = *it; + + if (ml->name == mode->name && ml->set == status && ml->param == param) + return true; + } + + return false; + } + + bool SetMLock(ChannelMode *mode, bool status, const Anope::string ¶m, Anope::string setter, time_t created = Anope::CurTime) anope_override + { + if (!mode) + return false; + + RemoveMLock(mode, status, param); + + if (setter.empty()) + setter = ci->GetFounder() ? ci->GetFounder()->display : "Unknown"; + + ModeLock *ml = new ModeLockImpl(); + ml->ci = ci->name; + ml->set = status; + ml->name = mode->name; + ml->param = param; + ml->setter = setter; + ml->created = created; + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnMLock, MOD_RESULT, (this->ci, ml)); + if (MOD_RESULT == EVENT_STOP) + { + delete ml; + return false; + } + + this->mlocks->push_back(ml); + return true; + } + + bool RemoveMLock(ChannelMode *mode, bool status, const Anope::string ¶m = "") anope_override + { + if (!mode) + return false; + + for (ModeList::iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) + { + ModeLock *m = *it; + + if (m->name == mode->name) + { + // For list or status modes, we must check the parameter + if (mode->type == MODE_LIST || mode->type == MODE_STATUS) + if (m->param != param) + continue; + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnUnMLock, MOD_RESULT, (this->ci, m)); + if (MOD_RESULT == EVENT_STOP) + break; + + delete m; + return true; + } + } + + return false; + } + + void RemoveMLock(ModeLock *mlock) anope_override + { + ModeList::iterator it = std::find(this->mlocks->begin(), this->mlocks->end(), mlock); + if (it != this->mlocks->end()) + this->mlocks->erase(it); + } + + void ClearMLock() anope_override + { + ModeList ml; + this->mlocks->swap(ml); + for (unsigned i = 0; i < ml.size(); ++i) + delete ml[i]; + } + + const ModeList &GetMLock() const anope_override + { + return this->mlocks; + } + + std::list<ModeLock *> GetModeLockList(const Anope::string &name) anope_override + { + std::list<ModeLock *> mlist; + for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) + { + ModeLock *m = *it; + if (m->name == name) + mlist.push_back(m); + } + return mlist; + } + + const ModeLock *GetMLock(const Anope::string &mname, const Anope::string ¶m = "") anope_override + { + for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) + { + ModeLock *m = *it; + + if (m->name == mname && m->param == param) + return m; + } + + return NULL; + } + + Anope::string GetMLockAsString(bool complete) const anope_override + { + Anope::string pos = "+", neg = "-", params; + + for (ModeList::const_iterator it = this->mlocks->begin(); it != this->mlocks->end(); ++it) + { + const ModeLock *ml = *it; + ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); + + if (!cm || cm->type == MODE_LIST || cm->type == MODE_STATUS) + continue; + + if (ml->set) + pos += cm->mchar; + else + neg += cm->mchar; + + if (complete && ml->set && !ml->param.empty() && cm->type == MODE_PARAM) + params += " " + ml->param; + } + + if (pos.length() == 1) + pos.clear(); + if (neg.length() == 1) + neg.clear(); + + return pos + neg + params; + } + + void Check() anope_override + { + if (this->mlocks->empty()) + ci->Shrink<ModeLocks>("modelocks"); + } +}; + +void ModeLockImpl::Serialize(Serialize::Data &data) const +{ + data["ci"] << this->ci; + data["set"] << this->set; + data["name"] << this->name; + data["param"] << this->param; + data["setter"] << this->setter; + data.SetType("created", Serialize::Data::DT_INT); data["created"] << this->created; +} + +Serializable* ModeLockImpl::Unserialize(Serializable *obj, Serialize::Data &data) +{ + Anope::string sci; + + data["ci"] >> sci; + + ChannelInfo *ci = ChannelInfo::Find(sci); + if (!ci) + return NULL; + + ModeLockImpl *ml; + if (obj) + ml = anope_dynamic_static_cast<ModeLockImpl *>(obj); + else + { + ml = new ModeLockImpl(); + ml->ci = ci->name; + } + + data["set"] >> ml->set; + data["created"] >> ml->created; + data["setter"] >> ml->setter; + data["name"] >> ml->name; + data["param"] >> ml->param; + + if (!obj) + ci->Require<ModeLocksImpl>("modelocks")->mlocks->push_back(ml); + + return ml; +} class CommandCSMode : public Command { @@ -28,21 +256,20 @@ class CommandCSMode : public Command const Anope::string ¶m = params.size() > 3 ? params[3] : ""; bool override = !source.AccessFor(ci).HasPriv("MODE"); + ModeLocks *modelocks = ci->Require<ModeLocks>("modelocks"); if ((subcommand.equals_ci("ADD") || subcommand.equals_ci("SET")) && !param.empty()) { /* If setting, remove the existing locks */ if (subcommand.equals_ci("SET")) { - const ChannelInfo::ModeList &mlocks = ci->GetMLock(); - for (ChannelInfo::ModeList::const_iterator it = mlocks.begin(), it_next; it != mlocks.end(); it = it_next) + const ModeLocks::ModeList mlocks = modelocks->GetMLock(); + for (ModeLocks::ModeList::const_iterator it = mlocks.begin(); it != mlocks.end(); ++it) { - const ModeLock *ml = it->second; + const ModeLock *ml = *it; ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); - it_next = it; - ++it_next; if (cm && cm->CanSet(source.GetUser())) - ci->RemoveMLock(cm, ml->set, ml->param); + modelocks->RemoveMLock(cm, ml->set, ml->param); } } @@ -86,7 +313,7 @@ class CommandCSMode : public Command source.Reply(_("List for mode %c is full."), cm->mchar); else { - ci->SetMLock(cm, adding, mode_param, source.GetNick()); + modelocks->SetMLock(cm, adding, mode_param, source.GetNick()); if (adding) { @@ -110,8 +337,8 @@ class CommandCSMode : public Command neg.clear(); Anope::string reply = pos + neg + pos_params + neg_params; - source.Reply(_("%s locked on %s."), ci->GetMLockAsString(true).c_str(), ci->name.c_str()); - Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to lock " << ci->GetMLockAsString(true); + source.Reply(_("%s locked on %s."), modelocks->GetMLockAsString(true).c_str(), ci->name.c_str()); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to lock " << modelocks->GetMLockAsString(true); if (ci->c) ci->c->CheckModes(); @@ -154,7 +381,7 @@ class CommandCSMode : public Command source.Reply(_("Missing parameter for mode %c."), cm->mchar); else { - if (ci->RemoveMLock(cm, adding, mode_param)) + if (modelocks->RemoveMLock(cm, adding, mode_param)) { if (!mode_param.empty()) mode_param = " " + mode_param; @@ -169,7 +396,7 @@ class CommandCSMode : public Command } else if (subcommand.equals_ci("LIST")) { - const ChannelInfo::ModeList &mlocks = ci->GetMLock(); + const ModeLocks::ModeList mlocks = modelocks->GetMLock(); if (mlocks.empty()) { source.Reply(_("Channel %s has no mode locks."), ci->name.c_str()); @@ -179,9 +406,9 @@ class CommandCSMode : public Command ListFormatter list; list.AddColumn("Mode").AddColumn("Param").AddColumn("Creator").AddColumn("Created"); - for (ChannelInfo::ModeList::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it) + for (ModeLocks::ModeList::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it) { - const ModeLock *ml = it->second; + const ModeLock *ml = *it; ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); if (!cm) continue; @@ -481,12 +708,125 @@ class CommandCSMode : public Command class CSMode : public Module { CommandCSMode commandcsmode; + ExtensibleItem<ModeLocksImpl> modelocks; + Serialize::Type modelocks_type; public: CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandcsmode(this) + commandcsmode(this), modelocks(this, "modelocks"), modelocks_type("ModeLock", ModeLockImpl::Unserialize) + { + + } + + EventReturn OnCheckModes(Channel *c) anope_override + { + if (!c->ci) + return EVENT_CONTINUE; + + ModeLocks *ml = modelocks.Get(c->ci); + if (ml) + for (ModeLocks::ModeList::const_iterator it = ml->GetMLock().begin(), it_end = ml->GetMLock().end(); it != it_end; ++it) + { + const ModeLock *ml = *it; + ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name); + if (!cm) + continue; + + if (cm->type == MODE_REGULAR) + { + if (!c->HasMode(cm->name) && ml->set) + c->SetMode(NULL, cm); + else if (c->HasMode(cm->name) && !ml->set) + c->RemoveMode(NULL, cm); + } + else if (cm->type == MODE_PARAM) + { + /* If the channel doesnt have the mode, or it does and it isn't set correctly */ + if (ml->set) + { + Anope::string param; + c->GetParam(cm->name, param); + + if (!c->HasMode(cm->name) || (!param.empty() && !ml->param.empty() && !param.equals_cs(ml->param))) + c->SetMode(NULL, cm, ml->param); + } + else + { + if (c->HasMode(cm->name)) + c->RemoveMode(NULL, cm); + } + + } + else if (cm->type == MODE_LIST) + { + if (ml->set) + c->SetMode(NULL, cm, ml->param); + else + c->RemoveMode(NULL, cm, ml->param); + } + } + } + + EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) anope_override + { + if (!c->ci) + return EVENT_CONTINUE; + + ModeLocks *ml = modelocks.Get(c->ci); + if (!ml) + return EVENT_CONTINUE; + + if (ml->HasMLock(mode, param, false)) + c->RemoveMode(c->ci->WhoSends(), mode, param); + } + + EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) anope_override + { + if (!c->ci) + return EVENT_CONTINUE; + + ModeLocks *ml = modelocks.Get(c->ci); + if (!ml) + return EVENT_CONTINUE; + + if (ml->HasMLock(mode, param, true)) + c->SetMode(c->ci->WhoSends(), mode, param); + } + + void OnCreateChan(ChannelInfo *ci) anope_override + { + ModeLocks *ml = modelocks.Require(ci); + Anope::string modes; + spacesepstream sep(Config->GetModule(this)->Get<const Anope::string>("mlock", "+nrt")); + if (sep.GetToken(modes)) + { + bool add = true; + for (unsigned i = 0; i < modes.length(); ++i) + { + if (modes[i] == '+') + add = true; + else if (modes[i] == '-') + add = false; + else + { + ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); + Anope::string param; + if (cm && (cm->type == MODE_REGULAR || sep.GetToken(param))) + ml->SetMLock(cm, add, param); + } + } + } + ml->Check(); + } + + void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_hidden) anope_override { + if (!show_hidden) + return; + ModeLocks *ml = modelocks.Get(ci); + if (ml) + info[_("Mode lock")] = ml->GetMLockAsString(true); } }; diff --git a/modules/commands/cs_seen.cpp b/modules/commands/cs_seen.cpp index 877fc31b7..9c0b6abfb 100644 --- a/modules/commands/cs_seen.cpp +++ b/modules/commands/cs_seen.cpp @@ -95,7 +95,7 @@ static bool ShouldHide(const Anope::string &channel, User *u) if (targetchan && targetchan->HasMode("SECRET")) return true; - else if (targetchan_ci && targetchan_ci->HasExt("PRIVATE")) + else if (targetchan_ci && targetchan_ci->HasExt("CS_PRIVATE")) return true; else if (u && u->HasMode("PRIV")) return true; diff --git a/modules/commands/cs_set.cpp b/modules/commands/cs_set.cpp index d07e544cc..d392f6b1d 100644 --- a/modules/commands/cs_set.cpp +++ b/modules/commands/cs_set.cpp @@ -10,6 +10,7 @@ */ #include "module.h" +#include "modules/cs_mode.h" class CommandCSSet : public Command { @@ -86,13 +87,13 @@ class CommandCSSetAutoOp : public Command if (params[1].equals_ci("ON")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable autoop"; - ci->Shrink("NOAUTOOP"); + ci->Shrink<bool>("NOAUTOOP"); source.Reply(_("Services will now automatically give modes to users in \002%s\002."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable autoop"; - ci->ExtendMetadata("NOAUTOOP"); + ci->Extend<bool>("NOAUTOOP"); source.Reply(_("Services will no longer automatically give modes to users in \002%s\002."), ci->name.c_str()); } else @@ -203,14 +204,14 @@ class CommandCSSetChanstats : public Command if (params[1].equals_ci("ON")) { - ci->ExtendMetadata("STATS"); + ci->Extend<bool>("CS_STATS"); source.Reply(_("Chanstats statistics are now enabled for this channel.")); Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable chanstats"; } else if (params[1].equals_ci("OFF")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable chanstats"; - ci->Shrink("STATS"); + ci->Shrink<bool>("CS_STATS"); source.Reply(_("Chanstats statistics are now disabled for this channel.")); } else @@ -344,64 +345,6 @@ class CommandCSSetFounder : public Command } }; -class CommandCSSetKeepTopic : public Command -{ - public: - CommandCSSetKeepTopic(Module *creator, const Anope::string &cname = "chanserv/set/keeptopic") : Command(creator, cname, 2, 2) - { - this->SetDesc(_("Retain topic when channel is not in use")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - EventReturn MOD_RESULT; - FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1])); - if (MOD_RESULT == EVENT_STOP) - return; - - if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keeptopic"; - ci->ExtendMetadata("KEEPTOPIC"); - source.Reply(_("Topic retention option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keeptopic"; - ci->Shrink("KEEPTOPIC"); - source.Reply(_("Topic retention option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "KEEPTOPIC"); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the \002topic retention\002 option for a\n" - "channel. When \002%s\002 is set, the topic for the\n" - "channel will be remembered by %s even after the\n" - "last user leaves the channel, and will be restored the\n" - "next time the channel is created."), this->name.c_str(), source.service->nick.c_str()); - return true; - } -}; - class CommandCSSetPeace : public Command { public: @@ -433,13 +376,13 @@ class CommandCSSetPeace : public Command if (params[1].equals_ci("ON")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable peace"; - ci->ExtendMetadata("PEACE"); + ci->Extend<bool>("PEACE"); source.Reply(_("Peace option for %s is now \002on\002."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable peace"; - ci->Shrink("PEACE"); + ci->Shrink<bool>("PEACE"); source.Reply(_("Peace option for %s is now \002off\002."), ci->name.c_str()); } else @@ -495,7 +438,7 @@ class CommandCSSetPersist : public Command { if (!ci->HasExt("PERSIST")) { - ci->ExtendMetadata("PERSIST"); + ci->Extend<bool>("PERSIST"); /* Channel doesn't exist, create it */ if (!ci->c) @@ -535,7 +478,9 @@ class CommandCSSetPersist : public Command if (ci->c && !ci->c->HasMode("PERM")) ci->c->SetMode(NULL, cm); /* Add it to the channels mlock */ - ci->SetMLock(cm, true); + ModeLocks *ml = ci->Require<ModeLocks>("modelocks"); + if (ml) + ml->SetMLock(cm, true); } } @@ -546,7 +491,7 @@ class CommandCSSetPersist : public Command { if (ci->HasExt("PERSIST")) { - ci->Shrink("PERSIST"); + ci->Shrink<bool>("PERSIST"); /* Unset perm mode */ if (cm) @@ -554,7 +499,9 @@ class CommandCSSetPersist : public Command if (ci->c && ci->c->HasMode("PERM")) ci->c->RemoveMode(NULL, cm); /* Remove from mlock */ - ci->RemoveMLock(cm, true); + ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks"); + if (ml) + ml->RemoveMLock(cm, true); } /* No channel mode, no BotServ, but using ChanServ as the botserv bot @@ -608,68 +555,6 @@ class CommandCSSetPersist : public Command } }; -class CommandCSSetPrivate : public Command -{ - public: - CommandCSSetPrivate(Module *creator, const Anope::string &cname = "chanserv/set/private") : Command(creator, cname, 2, 2) - { - this->SetDesc(_("Hide channel from the LIST command")); - this->SetSyntax(_("\037channel\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - ChannelInfo *ci = ChannelInfo::Find(params[0]); - if (ci == NULL) - { - source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); - return; - } - - EventReturn MOD_RESULT; - FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1])); - if (MOD_RESULT == EVENT_STOP) - return; - - if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration")) - { - source.Reply(ACCESS_DENIED); - return; - } - - if (params[1].equals_ci("ON")) - { - Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable private"; - ci->ExtendMetadata("PRIVATE"); - source.Reply(_("Private option for %s is now \002on\002."), ci->name.c_str()); - } - else if (params[1].equals_ci("OFF")) - { - Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable private"; - ci->Shrink("PRIVATE"); - source.Reply(_("Private option for %s is now \002off\002."), ci->name.c_str()); - } - else - this->OnSyntaxError(source, "PRIVATE"); - - return; - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Enables or disables the \002private\002 option for a channel.")); - - BotInfo *bi; - Anope::string cmd; - if (Command::FindCommandFromService("chanserv/list", bi, cmd)) - source.Reply(_("When \002private\002 is set, the channel will not appear in\n" - "%s's %s command."), bi->nick.c_str(), cmd.c_str()); - return true; - } -}; - class CommandCSSetRestricted : public Command { public: @@ -702,13 +587,13 @@ class CommandCSSetRestricted : public Command if (params[1].equals_ci("ON")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable restricted"; - ci->ExtendMetadata("RESTRICTED"); + ci->Extend<bool>("RESTRICTED"); source.Reply(_("Restricted access option for %s is now \002on\002."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable restricted"; - ci->Shrink("RESTRICTED"); + ci->Shrink<bool>("RESTRICTED"); source.Reply(_("Restricted access option for %s is now \002off\002."), ci->name.c_str()); } else @@ -758,13 +643,13 @@ class CommandCSSetSecure : public Command if (params[1].equals_ci("ON")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure"; - ci->ExtendMetadata("SECURE"); + ci->Extend<bool>("CS_SECURE"); source.Reply(_("Secure option for %s is now \002on\002."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure"; - ci->Shrink("SECURE"); + ci->Shrink<bool>("CS_SECURE"); source.Reply(_("Secure option for %s is now \002off\002."), ci->name.c_str()); } else @@ -816,13 +701,13 @@ class CommandCSSetSecureFounder : public Command if (params[1].equals_ci("ON")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure founder"; - ci->ExtendMetadata("SECUREFOUNDER"); + ci->Extend<bool>("SECUREFOUNDER"); source.Reply(_("Secure founder option for %s is now \002on\002."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure founder"; - ci->Shrink("SECUREFOUNDER"); + ci->Shrink<bool>("SECUREFOUNDER"); source.Reply(_("Secure founder option for %s is now \002off\002."), ci->name.c_str()); } else @@ -874,13 +759,13 @@ class CommandCSSetSecureOps : public Command if (params[1].equals_ci("ON")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure ops"; - ci->ExtendMetadata("SECUREOPS"); + ci->Extend<bool>("SECUREOPS"); source.Reply(_("Secure ops option for %s is now \002on\002."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure ops"; - ci->Shrink("SECUREOPS"); + ci->Shrink<bool>("SECUREOPS"); source.Reply(_("Secure ops option for %s is now \002off\002."), ci->name.c_str()); } else @@ -904,7 +789,7 @@ class CommandCSSetSignKick : public Command CommandCSSetSignKick(Module *creator, const Anope::string &cname = "chanserv/set/signkick") : Command(creator, cname, 2, 2) { this->SetDesc(_("Sign kicks that are done with the KICK command")); - this->SetSyntax(_("\037channel\037 SIGNKICK {ON | LEVEL | OFF}")); + this->SetSyntax(_("\037channel\037 {ON | LEVEL | OFF}")); } void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override @@ -929,23 +814,23 @@ class CommandCSSetSignKick : public Command if (params[1].equals_ci("ON")) { - ci->ExtendMetadata("SIGNKICK"); - ci->Shrink("SIGNKICK_LEVEL"); + ci->Extend<bool>("SIGNKICK"); + ci->Shrink<bool>("SIGNKICK_LEVEL"); source.Reply(_("Signed kick option for %s is now \002on\002."), ci->name.c_str()); Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick"; } else if (params[1].equals_ci("LEVEL")) { - ci->ExtendMetadata("SIGNKICK_LEVEL"); - ci->Shrink("SIGNKICK"); + ci->Extend<bool>("SIGNKICK_LEVEL"); + ci->Shrink<bool>("SIGNKICK"); source.Reply(_("Signed kick option for %s is now \002on\002, but depends of the\n" "level of the user that is using the command."), ci->name.c_str()); Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick level"; } else if (params[1].equals_ci("OFF")) { - ci->Shrink("SIGNKICK"); - ci->Shrink("SIGNKICK_LEVEL"); + ci->Shrink<bool>("SIGNKICK"); + ci->Shrink<bool>("SIGNKICK_LEVEL"); source.Reply(_("Signed kick option for %s is now \002off\002."), ci->name.c_str()); Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable sign kick"; } @@ -1075,13 +960,13 @@ class CommandCSSetNoexpire : public Command if (params[1].equals_ci("ON")) { Log(LOG_ADMIN, source, this, ci) << "to enable noexpire"; - ci->ExtendMetadata("NO_EXPIRE"); + ci->Extend<bool>("CS_NO_EXPIRE"); source.Reply(_("Channel %s \002will not\002 expire."), ci->name.c_str()); } else if (params[1].equals_ci("OFF")) { Log(LOG_ADMIN, source, this, ci) << "to disable noexpire"; - ci->Shrink("NO_EXPIRE"); + ci->Shrink<bool>("CS_NO_EXPIRE"); source.Reply(_("Channel %s \002will\002 expire."), ci->name.c_str()); } else @@ -1102,16 +987,17 @@ class CommandCSSetNoexpire : public Command class CSSet : public Module { + SerializableExtensibleItem<bool> persist, noautoop, stats, peace, securefounder, + restricted, secure, secureops, signkick, signkick_level, noexpire; + CommandCSSet commandcsset; CommandCSSetAutoOp commandcssetautoop; CommandCSSetBanType commandcssetbantype; CommandCSSetChanstats commandcssetchanstats; CommandCSSetDescription commandcssetdescription; CommandCSSetFounder commandcssetfounder; - CommandCSSetKeepTopic commandcssetkeeptopic; CommandCSSetPeace commandcssetpeace; CommandCSSetPersist commandcssetpersist; - CommandCSSetPrivate commandcssetprivate; CommandCSSetRestricted commandcssetrestricted; CommandCSSetSecure commandcssetsecure; CommandCSSetSecureFounder commandcssetsecurefounder; @@ -1122,17 +1008,27 @@ class CSSet : public Module public: CSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), + persist(this, "PERSIST"), noautoop(this, "NOAUTOOP"), stats(this, "CS_STATS"), peace(this, "PEACE"), + securefounder(this, "SECUREFOUNDER"), restricted(this, "RESTRICTED"), + secure(this, "CS_SECURE"), secureops(this, "SECUREOPS"), signkick(this, "SIGNKICK"), + signkick_level(this, "SIGNKICK_LEVEL"), noexpire(this, "CS_NO_EXPIRE"), + commandcsset(this), commandcssetautoop(this), commandcssetbantype(this), commandcssetchanstats(this), - commandcssetdescription(this), commandcssetfounder(this), commandcssetkeeptopic(this), - commandcssetpeace(this), commandcssetpersist(this), commandcssetprivate(this), commandcssetrestricted(this), + commandcssetdescription(this), commandcssetfounder(this), + commandcssetpeace(this), commandcssetpersist(this), commandcssetrestricted(this), commandcssetsecure(this), commandcssetsecurefounder(this), commandcssetsecureops(this), commandcssetsignkick(this), commandcssetsuccessor(this), commandcssetnoexpire(this) { } + void OnCreateChan(ChannelInfo *ci) anope_override + { + ci->bantype = Config->GetModule(this)->Get<int>("defbantype", "2"); + } + EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override { - if (!c->ci || !c->ci->HasExt("RESTRICTED") || c->MatchesList(u, "EXCEPT")) + if (!c->ci || !restricted.HasExt(c->ci) || c->MatchesList(u, "EXCEPT")) return EVENT_CONTINUE; if (c->ci->AccessFor(u).empty() && (!c->ci->GetFounder() || u->Account() != c->ci->GetFounder())) @@ -1143,30 +1039,30 @@ class CSSet : public Module void OnDelChan(ChannelInfo *ci) anope_override { - if (ci->c && ci->HasExt("PERSIST")) + if (ci->c && persist.HasExt(ci)) ci->c->RemoveMode(ci->WhoSends(), "PERM", "", false); - ci->Shrink("PERSIST"); + persist.Unset(ci); } - EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, const Anope::string &mname, const Anope::string ¶m) anope_override + EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) anope_override { /* Channel mode +P or so was set, mark this channel as persistent */ - if (mname == "PERM" && c->ci) + if (mode->name == "PERM" && c->ci) { - c->ci->ExtendMetadata("PERSIST"); + persist.Set(c->ci, true); } return EVENT_CONTINUE; } - EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, const Anope::string &mname, const Anope::string ¶m) anope_override + EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) anope_override { - if (mname == "PERM") + if (mode->name == "PERM") { if (c->ci) - c->ci->Shrink("PERSIST"); + persist.Unset(c->ci); - if (c->users.empty() && !c->syncing && c->CheckDelete()) + if (c->CheckDelete()) { delete c; return EVENT_STOP; @@ -1178,14 +1074,14 @@ class CSSet : public Module EventReturn OnCheckDelete(Channel *c) anope_override { - if (c->ci && c->ci->HasExt("PERSIST")) + if (c->ci && persist.HasExt(c->ci)) return EVENT_STOP; return EVENT_CONTINUE; } void OnJoinChannel(User *u, Channel *c) anope_override { - if (c->ci && c->ci->HasExt("PERSIST") && c->creation_time > c->ci->time_registered) + if (c->ci && persist.HasExt(c->ci) && c->creation_time > c->ci->time_registered) { Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->time_registered; c->creation_time = c->ci->time_registered; @@ -1198,10 +1094,47 @@ class CSSet : public Module { if (chan->ci) { - give_modes &= !chan->ci->HasExt("NOAUTOOP"); - take_modes |= chan->ci->HasExt("SECUREOPS"); + if (noautoop.HasExt(chan->ci)) + give_modes = false; + if (secureops.HasExt(chan->ci)) + take_modes = true; } } + + void OnPreChanExpire(ChannelInfo *ci, bool &expire) anope_override + { + if (noexpire.HasExt(ci)) + expire = false; + } + + void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override + { + if (!show_all) + return; + + if (peace.HasExt(ci)) + info.AddOption(_("Peace")); + if (restricted.HasExt(ci)) + info.AddOption(_("Restricted Access")); + if (secure.HasExt(ci)) + info.AddOption(_("Secure")); + if (securefounder.HasExt(ci)) + info.AddOption(_("Secure Founder")); + if (secureops.HasExt(ci)) + info.AddOption(_("Secure Ops")); + if (signkick.HasExt(ci) || signkick_level.HasExt(ci)) + info.AddOption(_("Signed kicks")); + if (persist.HasExt(ci)) + info.AddOption(_("Persistent")); + if (noexpire.HasExt(ci)) + info.AddOption(_("No expire")); + if (stats.HasExt(ci)) + info.AddOption(_("Chanstats")); + + time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d"); + if (!noexpire.HasExt(ci) && chanserv_expire && !Anope::NoExpire) + info["Expires on"] = Anope::strftime(ci->last_used + chanserv_expire); + } }; MODULE_INIT(CSSet) diff --git a/modules/commands/cs_set_misc.cpp b/modules/commands/cs_set_misc.cpp index 21ebec815..5467e2b4e 100644 --- a/modules/commands/cs_set_misc.cpp +++ b/modules/commands/cs_set_misc.cpp @@ -10,14 +10,24 @@ #include "module.h" +static Module *me; + static std::map<Anope::string, Anope::string> descriptions; -struct CSMiscData : ExtensibleItem, Serializable +struct CSMiscData; +static Anope::map<ExtensibleItem<CSMiscData> *> items; +static ExtensibleItem<CSMiscData> *GetItem(const Anope::string &name); + +struct CSMiscData : Serializable { Serialize::Reference<ChannelInfo> ci; Anope::string name; Anope::string data; + CSMiscData(Extensible *obj) : Serializable("CSMiscData"), ci(anope_dynamic_static_cast<ChannelInfo *>(obj)) + { + } + CSMiscData(ChannelInfo *c, const Anope::string &n, const Anope::string &d) : Serializable("CSMiscData"), ci(c), name(n), data(d) { } @@ -41,7 +51,7 @@ struct CSMiscData : ExtensibleItem, Serializable if (ci == NULL) return NULL; - CSMiscData *d; + CSMiscData *d = NULL; if (obj) { d = anope_dynamic_static_cast<CSMiscData *>(obj); @@ -51,14 +61,27 @@ struct CSMiscData : ExtensibleItem, Serializable } else { - d = new CSMiscData(ci, sname, sdata); - ci->Extend(sname, d); + ExtensibleItem<CSMiscData> *item = GetItem(sname); + if (item) + d = item->Set(ci, CSMiscData(ci, sname, sdata)); } return d; } }; +static ExtensibleItem<CSMiscData> *GetItem(const Anope::string &name) +{ + ExtensibleItem<CSMiscData>* &it = items[name]; + if (!it) + try + { + it = new ExtensibleItem<CSMiscData>(me, name); + } + catch (const ModuleException &) { } + return it; +} + static Anope::string GetAttribute(const Anope::string &command) { size_t sp = command.rfind(' '); @@ -97,14 +120,20 @@ class CommandCSSetMisc : public Command Anope::string scommand = GetAttribute(source.command); Anope::string key = "cs_set_misc:" + scommand; - ci->Shrink(key); + ExtensibleItem<CSMiscData> *item = GetItem(key); + if (item == NULL) + return; + if (params.size() > 1) { - ci->Extend(key, new CSMiscData(ci, key, params[1])); + item->Set(ci, CSMiscData(ci, key, params[1])); source.Reply(CHAN_SETTING_CHANGED, scommand.c_str(), ci->name.c_str(), params[1].c_str()); } else + { + item->Unset(ci); source.Reply(CHAN_SETTING_UNSET, scommand.c_str(), ci->name.c_str()); + } } void OnServHelp(CommandSource &source) anope_override @@ -136,6 +165,7 @@ class CSSetMisc : public Module CSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), csmiscdata_type("CSMiscData", CSMiscData::Unserialize), commandcssetmisc(this) { + me = this; } void OnReload(Configuration::Conf *conf) anope_override @@ -161,17 +191,14 @@ class CSSetMisc : public Module void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool ShowHidden) anope_override { - std::deque<Anope::string> list; - ci->GetExtList(list); - - for (unsigned i = 0; i < list.size(); ++i) + Anope::map<ExtensibleItem<CSMiscData> *> items; + for (Anope::map<ExtensibleItem<CSMiscData> *>::iterator it = items.begin(); it != items.end(); ++it) { - if (list[i].find("cs_set_misc:") != 0) - continue; - - CSMiscData *data = ci->GetExt<CSMiscData *>(list[i]); + ExtensibleItem<CSMiscData> *e = it->second; + CSMiscData *data = e->Get(ci); + if (data != NULL) - info[list[i].substr(12).replace_all_cs("_", " ")] = data->data; + info[e->name.substr(12).replace_all_cs("_", " ")] = data->data; } } }; diff --git a/modules/commands/cs_suspend.cpp b/modules/commands/cs_suspend.cpp index e366e5dc0..b7ffccda6 100644 --- a/modules/commands/cs_suspend.cpp +++ b/modules/commands/cs_suspend.cpp @@ -10,6 +10,45 @@ */ #include "module.h" +#include "modules/cs_suspend.h" + +struct CSSuspendInfoImpl : CSSuspendInfo, Serializable +{ + CSSuspendInfoImpl(Extensible *) : Serializable("CSSuspendInfo") { } + + void Serialize(Serialize::Data &data) const anope_override + { + data["chan"] << chan; + data["by"] << by; + data["reason"] << reason; + data["time"] << time; + data["expires"] << expires; + } + + static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) + { + Anope::string schan; + data["chan"] >> schan; + + CSSuspendInfoImpl *si; + if (obj) + si = anope_dynamic_static_cast<CSSuspendInfoImpl *>(obj); + else + { + ChannelInfo *ci = ChannelInfo::Find(schan); + if (!ci) + return NULL; + si = ci->Extend<CSSuspendInfoImpl>("cs_suspend"); + data["chan"] >> si->chan; + } + + data["bi"] >> si->by; + data["reason"] >> si->reason; + data["time"] >> si->time; + data["expires"] >> si->expires; + return si; + } +}; class CommandCSSuspend : public Command { @@ -46,11 +85,12 @@ class CommandCSSuspend : public Command return; } - ci->ExtendMetadata("SUSPENDED"); - ci->ExtendMetadata("suspend:by", source.GetNick()); - if (!reason.empty()) - ci->ExtendMetadata("suspend:reason", reason); - ci->ExtendMetadata("suspend:time", stringify(Anope::CurTime)); + CSSuspendInfo *si = ci->Extend<CSSuspendInfo>("cs_suspend"); + si->chan = ci->name; + si->by = source.GetNick(); + si->reason = reason; + si->time = Anope::CurTime; + si->expires = expiry_secs ? expiry_secs + Anope::CurTime : 0; if (ci->c) { @@ -68,15 +108,10 @@ class CommandCSSuspend : public Command ci->c->Kick(NULL, users[i], "%s", !reason.empty() ? reason.c_str() : Language::Translate(users[i], _("This channel has been suspended."))); } - if (expiry_secs > 0) - ci->ExtendMetadata("suspend:expire", stringify(Anope::CurTime + expiry_secs)); - Log(LOG_ADMIN, source, this, ci) << (!reason.empty() ? reason : "No reason") << ", expires in " << (expiry_secs ? Anope::strftime(Anope::CurTime + expiry_secs) : "never"); source.Reply(_("Channel \002%s\002 is now suspended."), ci->name.c_str()); FOREACH_MOD(OnChanSuspend, (ci)); - - return; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -118,21 +153,16 @@ class CommandCSUnSuspend : public Command } /* Only UNSUSPEND already suspended channels */ - if (!ci->HasExt("SUSPENDED")) + CSSuspendInfo *si = ci->GetExt<CSSuspendInfo>("cs_suspend"); + if (!si) { source.Reply(_("Channel \002%s\002 isn't suspended."), ci->name.c_str()); return; } - Anope::string *by = ci->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:by"), *reason = ci->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:reason"); - if (by != NULL) - Log(LOG_ADMIN, source, this, ci) << " which was suspended by " << *by << " for: " << (reason && !reason->empty() ? *reason : "No reason"); + Log(LOG_ADMIN, source, this, ci) << " which was suspended by " << si->by << " for: " << (!si->reason.empty() ? si->reason : "No reason"); - ci->Shrink("SUSPENDED"); - ci->Shrink("suspend:by"); - ci->Shrink("suspend:reason"); - ci->Shrink("suspend:expire"); - ci->Shrink("suspend:time"); + ci->Shrink<CSSuspendInfo>("cs_suspend"); source.Reply(_("Channel \002%s\002 is now released."), ci->name.c_str()); @@ -155,66 +185,73 @@ class CSSuspend : public Module { CommandCSSuspend commandcssuspend; CommandCSUnSuspend commandcsunsuspend; + ExtensibleItem<CSSuspendInfoImpl> suspend; + Serialize::Type suspend_type; public: CSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandcssuspend(this), commandcsunsuspend(this) + commandcssuspend(this), commandcsunsuspend(this), suspend(this, "cs_suspend"), + suspend_type("CSSuspendInfo", CSSuspendInfoImpl::Unserialize) { - } void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_hidden) anope_override { - if (ci->HasExt("SUSPENDED")) + CSSuspendInfo *si = suspend.Get(ci); + if (si) { - Anope::string *by = ci->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:by"), *reason = ci->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:reason"), *t = ci->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:time"); info["Suspended"] = "This channel is \2suspended\2."; - if (by) - info["Suspended by"] = *by; - if (reason) - info["Suspend reason"] = *reason; - if (t) - info["Suspended on"] = Anope::strftime(convertTo<time_t>(*t), source.GetAccount(), true); + if (!si->by.empty()) + info["Suspended by"] = si->by; + if (!si->reason.empty()) + info["Suspend reason"] = si->reason; + if (si->time) + info["Suspended on"] = Anope::strftime(si->time, source.GetAccount(), true); + if (si->expires) + info["Suspended expires"] = Anope::strftime(si->expires, source.GetAccount(), true); } } void OnPreChanExpire(ChannelInfo *ci, bool &expire) anope_override { - if (!ci->HasExt("SUSPENDED")) + CSSuspendInfo *si = suspend.Get(ci); + if (!si) return; expire = false; - Anope::string *str = ci->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:expire"); - if (str == NULL) + if (!si->expires) return; - try + if (si->expires < Anope::CurTime) { - time_t when = convertTo<time_t>(*str); - if (when < Anope::CurTime) - { - ci->last_used = Anope::CurTime; - ci->Shrink("SUSPENDED"); - ci->Shrink("suspend:expire"); - ci->Shrink("suspend:by"); - ci->Shrink("suspend:reason"); - ci->Shrink("suspend:time"); - - Log(this) << "Expiring suspend for " << ci->name; - } + ci->last_used = Anope::CurTime; + suspend.Unset(ci); + + Log(this) << "Expiring suspend for " << ci->name; } - catch (const ConvertException &) { } } EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override { - if (u->HasMode("OPER") || !c->ci || !c->ci->HasExt("SUSPENDED")) + if (u->HasMode("OPER") || !c->ci || !suspend.HasExt(c->ci)) return EVENT_CONTINUE; reason = Language::Translate(u, _("This channel may not be used.")); return EVENT_STOP; } + + EventReturn OnChanDrop(CommandSource &source, ChannelInfo *ci) anope_override + { + CSSuspendInfo *si = suspend.Get(ci); + if (si && !source.HasCommand("chanserv/drop")) + { + source.Reply(CHAN_X_SUSPENDED, ci->name.c_str()); + return EVENT_STOP; + } + + return EVENT_CONTINUE; + } }; MODULE_INIT(CSSuspend) diff --git a/modules/commands/cs_topic.cpp b/modules/commands/cs_topic.cpp index 294689864..39ddfe8f9 100644 --- a/modules/commands/cs_topic.cpp +++ b/modules/commands/cs_topic.cpp @@ -10,9 +10,70 @@ */ #include "module.h" +#include "modules/cs_mode.h" + +class CommandCSSetKeepTopic : public Command +{ + public: + CommandCSSetKeepTopic(Module *creator, const Anope::string &cname = "chanserv/set/keeptopic") : Command(creator, cname, 2, 2) + { + this->SetDesc(_("Retain topic when channel is not in use")); + this->SetSyntax(_("\037channel\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1])); + if (MOD_RESULT == EVENT_STOP) + return; + + if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (params[1].equals_ci("ON")) + { + Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keeptopic"; + ci->Extend<bool>("KEEPTOPIC"); + source.Reply(_("Topic retention option for %s is now \002on\002."), ci->name.c_str()); + } + else if (params[1].equals_ci("OFF")) + { + Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keeptopic"; + ci->Shrink<bool>("KEEPTOPIC"); + source.Reply(_("Topic retention option for %s is now \002off\002."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, "KEEPTOPIC"); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Enables or disables the \002topic retention\002 option for a\n" + "channel. When \002%s\002 is set, the topic for the\n" + "channel will be remembered by %s even after the\n" + "last user leaves the channel, and will be restored the\n" + "next time the channel is created."), source.command.c_str(), source.service->nick.c_str()); + return true; + } +}; class CommandCSTopic : public Command { + ExtensibleRef<bool> topiclock; + void Lock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) { EventReturn MOD_RESULT; @@ -20,7 +81,7 @@ class CommandCSTopic : public Command if (MOD_RESULT == EVENT_STOP) return; - ci->ExtendMetadata("TOPICLOCK"); + topiclock->Set(ci, true); source.Reply(_("Topic lock option for %s is now \002on\002."), ci->name.c_str()); } @@ -31,7 +92,7 @@ class CommandCSTopic : public Command if (MOD_RESULT == EVENT_STOP) return; - ci->Shrink("TOPICLOCK"); + topiclock->Unset(ci); source.Reply(_("Topic lock option for %s is now \002off\002."), ci->name.c_str()); } @@ -39,11 +100,11 @@ class CommandCSTopic : public Command { const Anope::string &topic = params.size() > 2 ? params[2] : ""; - bool has_topiclock = ci->HasExt("TOPICLOCK"); - ci->Shrink("TOPICLOCK"); + bool *has_topiclock = topiclock->Get(ci); + topiclock->Unset(ci); ci->c->ChangeTopic(source.GetNick(), topic, Anope::CurTime); if (has_topiclock) - ci->ExtendMetadata("TOPICLOCK"); + topiclock->Set(ci, *has_topiclock); bool override = !source.AccessFor(ci).HasPriv("TOPIC"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << (!topic.empty() ? "to change the topic to: " : "to unset the topic") << (!topic.empty() ? topic : ""); @@ -71,7 +132,8 @@ class CommandCSTopic : public Command } public: - CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 2, 3) + CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 2, 3), + topiclock("TOPICLOCK") { this->SetDesc(_("Manipulate the topic of the specified channel")); this->SetSyntax(_("\037channel\037 SET [\037topic\037]")); @@ -120,13 +182,66 @@ class CommandCSTopic : public Command class CSTopic : public Module { CommandCSTopic commandcstopic; + CommandCSSetKeepTopic commandcssetkeeptopic; + + SerializableExtensibleItem<bool> topiclock, keeptopic; public: CSTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandcstopic(this) + commandcstopic(this), commandcssetkeeptopic(this), topiclock(this, "TOPICLOCK"), keeptopic(this, "KEEPTOPIC") { } + + void OnChannelSync(Channel *c) anope_override + { + if (Me && Me->IsSynced() && c->ci) + { + /* Update channel topic */ + if ((topiclock.HasExt(c->ci) || keeptopic.HasExt(c->ci)) && c->ci->last_topic != c->topic) + { + c->ChangeTopic(!c->ci->last_topic_setter.empty() ? c->ci->last_topic_setter : c->ci->WhoSends()->nick, c->ci->last_topic, c->ci->last_topic_time ? c->ci->last_topic_time : Anope::CurTime); + } + } + } + + void OnTopicUpdated(Channel *c, const Anope::string &user, const Anope::string &topic) anope_override + { + if (!c->ci) + return; + + /* We only compare the topics here, not the time or setter. This is because some (old) IRCds do not + * allow us to set the topic as someone else, meaning we have to bump the TS and change the setter to us. + * This desyncs what is really set with what we have stored, and we end up resetting the topic often when + * it is not required + */ + if (topiclock.HasExt(c->ci) && c->ci->last_topic != c->topic) + { + c->ChangeTopic(c->ci->last_topic_setter, c->ci->last_topic, c->ci->last_topic_time); + } + else + { + c->ci->last_topic = c->topic; + c->ci->last_topic_setter = c->topic_setter; + c->ci->last_topic_time = c->topic_ts; + } + } + + void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) + { + if (keeptopic.HasExt(ci)) + info.AddOption(_("Topic Retention")); + if (topiclock.HasExt(ci)) + info.AddOption(_("Topic Lock")); + + ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks"); + const ModeLock *secret = ml ? ml->GetMLock("SECRET") : NULL; + if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode("SECRET"))))) + { + info["Last topic"] = ci->last_topic; + info["Topic set by"] = ci->last_topic_setter; + } + } }; MODULE_INIT(CSTopic) diff --git a/modules/commands/greet.cpp b/modules/commands/greet.cpp new file mode 100644 index 000000000..dd3fce66f --- /dev/null +++ b/modules/commands/greet.cpp @@ -0,0 +1,210 @@ +/* + * + * (C) 2003-2013 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 CommandBSSetGreet : public Command +{ + public: + CommandBSSetGreet(Module *creator, const Anope::string &sname = "botserv/set/greet") : Command(creator, sname, 2, 2) + { + this->SetDesc(_("Enable greet messages")); + this->SetSyntax(_("\037channel\037 {\037ON|OFF\037}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + ChannelInfo *ci = ChannelInfo::Find(params[0]); + const Anope::string &value = params[1]; + + if (ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str()); + return; + } + + if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET")) + { + source.Reply(ACCESS_DENIED); + return; + } + + if (Anope::ReadOnly) + { + source.Reply(_("Sorry, bot option setting is temporarily disabled.")); + return; + } + + if (value.equals_ci("ON")) + { + bool override = !source.AccessFor(ci).HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable greets"; + + ci->Extend<bool>("BS_GREET"); + source.Reply(_("Greet mode is now \002on\002 on channel %s."), ci->name.c_str()); + } + else if (value.equals_ci("OFF")) + { + bool override = !source.AccessFor(ci).HasPriv("SET"); + Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable greets"; + + ci->Shrink<bool>("BS_GREET"); + source.Reply(_("Greet mode is now \002off\002 on channel %s."), ci->name.c_str()); + } + else + this->OnSyntaxError(source, source.command); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(_(" \n" + "Enables or disables \002greet\002 mode on a channel.\n" + "When it is enabled, the bot will display greet\n" + "messages of users joining the channel, provided\n" + "they have enough access to the channel.")); + return true; + } +}; + +class CommandNSSetGreet : public Command +{ + public: + CommandNSSetGreet(Module *creator, const Anope::string &sname = "nickserv/set/greet", size_t min = 0) : Command(creator, sname, min, min + 1) + { + this->SetDesc(_("Associate a greet message with your nickname")); + this->SetSyntax(_("\037message\037")); + } + + void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) + { + const NickAlias *na = NickAlias::Find(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param)); + if (MOD_RESULT == EVENT_STOP) + return; + + if (!param.empty()) + { + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the greet of " << nc->display; + nc->Extend<Anope::string>("greet", param); + source.Reply(_("Greet message for \002%s\002 changed to \002%s\002."), nc->display.c_str(), param.c_str()); + } + else + { + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to unset the greet of " << nc->display; + nc->Shrink<Anope::string>("greet"); + source.Reply(_("Greet message for \002%s\002 unset."), nc->display.c_str()); + } + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + this->Run(source, source.nc->display, params.size() > 0 ? params[0] : ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Makes the given message the greet of your nickname, that\n" + "will be displayed when joining a channel that has GREET\n" + "option enabled, provided that you have the necessary\n" + "access on it.")); + return true; + } +}; + +class CommandNSSASetGreet : public CommandNSSetGreet +{ + public: + CommandNSSASetGreet(Module *creator) : CommandNSSetGreet(creator, "nickserv/saset/greet", 1) + { + this->ClearSyntax(); + this->SetSyntax(_("\037nickname\037 \037message\037")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + this->Run(source, params[0], params.size() > 1 ? params[1] : ""); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Makes the given message the greet of the nickname, that\n" + "will be displayed when joining a channel that has GREET\n" + "option enabled, provided that the user has the necessary\n" + "access on it.")); + return true; + } +}; + +class Greet : public Module +{ + /* channel setting for whether or not greet should be shown */ + SerializableExtensibleItem<bool> bs_greet; + /* user greets */ + SerializableExtensibleItem<Anope::string> ns_greet; + + CommandBSSetGreet commandbssetgreet; + CommandNSSetGreet commandnssetgreet; + CommandNSSASetGreet commandnssasetgreet; + + public: + Greet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), + bs_greet(this, "BS_GREET"), + ns_greet(this, "greet"), + commandbssetgreet(this), + commandnssetgreet(this), commandnssasetgreet(this) + { + } + + void OnJoinChannel(User *user, Channel *c) anope_override + { + /* 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->ci || !c->ci->bi || !user->server->IsSynced() || !user->Account()) + return; + + Anope::string *greet = ns_greet.Get(user->Account()); + if (bs_greet.HasExt(c->ci) && greet != NULL && !greet->empty() && c->FindUser(c->ci->bi) && c->ci->AccessFor(user).HasPriv("GREET")) + { + IRCD->SendPrivmsg(c->ci->bi, c->name, "[%s] %s", user->Account()->display.c_str(), greet->c_str()); + c->ci->bi->lastmsg = Anope::CurTime; + } + } + + void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override + { + Anope::string *greet = ns_greet.Get(na->nc); + if (greet != NULL) + info[_("Greet")] = *greet; + } + + void OnBotInfo(CommandSource &source, BotInfo *bi, ChannelInfo *ci, InfoFormatter &info) anope_override + { + if (bs_greet.HasExt(ci)) + info.AddOption(_("Greet")); + } +}; + +MODULE_INIT(Greet) diff --git a/modules/commands/hs_request.cpp b/modules/commands/hs_request.cpp index 2b1299f93..5203b086b 100644 --- a/modules/commands/hs_request.cpp +++ b/modules/commands/hs_request.cpp @@ -21,14 +21,14 @@ static ServiceReference<MemoServService> memoserv("MemoServService", "MemoServ") static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost); -struct HostRequest : ExtensibleItem, Serializable +struct HostRequest : Serializable { Anope::string nick; Anope::string ident; Anope::string host; time_t time; - HostRequest() : Serializable("HostRequest") { } + HostRequest(Extensible *) : Serializable("HostRequest") { } void Serialize(Serialize::Data &data) const anope_override { @@ -51,14 +51,15 @@ struct HostRequest : ExtensibleItem, Serializable if (obj) req = anope_dynamic_static_cast<HostRequest *>(obj); else - req = new HostRequest; - req->nick = na->nick; - data["ident"] >> req->ident; - data["host"] >> req->host; - data["time"] >> req->time; - - if (!obj) - na->Extend("hs_request", req); + req = na->Extend<HostRequest>("hostrequest"); + if (req) + { + req->nick = na->nick; + data["ident"] >> req->ident; + data["host"] >> req->host; + data["time"] >> req->time; + } + return req; } }; @@ -148,12 +149,12 @@ class CommandHSRequest : public Command return; } - HostRequest *req = new HostRequest; - req->nick = source.GetNick(); - req->ident = user; - req->host = host; - req->time = Anope::CurTime; - na->Extend("hs_request", req); + HostRequest req(na); + req.nick = source.GetNick(); + req.ident = user; + req.host = host; + req.time = Anope::CurTime; + na->Extend<HostRequest>("hostrequest", req); source.Reply(_("Your vHost has been requested.")); req_send_memos(owner, source, user, host); @@ -186,7 +187,7 @@ class CommandHSActivate : public Command const Anope::string &nick = params[0]; NickAlias *na = NickAlias::Find(nick); - HostRequest *req = na ? na->GetExt<HostRequest *>("hs_request") : NULL; + HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL; if (req) { na->SetVhost(req->ident, req->host, source.GetNick(), req->time); @@ -197,7 +198,7 @@ class CommandHSActivate : public Command source.Reply(_("vHost for %s has been activated."), na->nick.c_str()); Log(LOG_COMMAND, source, this) << "for " << na->nick << " for vhost " << (!req->ident.empty() ? req->ident + "@" : "") << req->host; - na->Shrink("hs_request"); + na->Shrink<HostRequest>("hostrequest"); } else source.Reply(_("No request for nick %s found."), nick.c_str()); @@ -231,10 +232,10 @@ class CommandHSReject : public Command const Anope::string &reason = params.size() > 1 ? params[1] : ""; NickAlias *na = NickAlias::Find(nick); - HostRequest *req = na ? na->GetExt<HostRequest *>("hs_request") : NULL; + HostRequest *req = na ? na->GetExt<HostRequest>("hostrequest") : NULL; if (req) { - na->Shrink("hs_request"); + na->Shrink<HostRequest>("hostrequest"); if (Config->GetModule(this->owner)->Get<bool>("memouser") && memoserv) { @@ -244,7 +245,7 @@ class CommandHSReject : public Command else message = _("[auto memo] Your requested vHost has been rejected."); - memoserv->Send(source.service->nick, nick, message, true); + memoserv->Send(source.service->nick, nick, Language::Translate(source.GetAccount(), message.c_str()), true); } source.Reply(_("vHost for %s has been rejected."), nick.c_str()); @@ -252,8 +253,6 @@ class CommandHSReject : public Command } else source.Reply(_("No request for nick %s found."), nick.c_str()); - - return; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -287,7 +286,7 @@ class CommandHSWaiting : public Command for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it) { const NickAlias *na = it->second; - HostRequest *hr = na->GetExt<HostRequest *>("hs_request"); + HostRequest *hr = na->GetExt<HostRequest>("hostrequest"); if (!hr) continue; @@ -334,24 +333,17 @@ class HSRequest : public Module CommandHSActivate commandhsactive; CommandHSReject commandhsreject; CommandHSWaiting commandhswaiting; + ExtensibleItem<HostRequest> hostrequest; public: HSRequest(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - request_type("HostRequest", HostRequest::Unserialize), commandhsrequest(this), commandhsactive(this), commandhsreject(this), commandhswaiting(this) + request_type("HostRequest", HostRequest::Unserialize), commandhsrequest(this), commandhsactive(this), + commandhsreject(this), commandhswaiting(this), hostrequest(this, "hostrequest") { if (!IRCD || !IRCD->CanSetVHost) throw ModuleException("Your IRCd does not support vhosts"); } - - ~HSRequest() - { - for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it) - { - NickAlias *na = it->second; - na->Shrink("hs_request"); - } - } }; static void req_send_memos(Module *me, CommandSource &source, const Anope::string &vIdent, const Anope::string &vHost) diff --git a/modules/commands/ms_info.cpp b/modules/commands/ms_info.cpp index 67a504117..909ff4c82 100644 --- a/modules/commands/ms_info.cpp +++ b/modules/commands/ms_info.cpp @@ -27,7 +27,7 @@ class CommandMSInfo : public Command const NickAlias *na = NULL; ChannelInfo *ci = NULL; const Anope::string &nname = !params.empty() ? params[0] : ""; - int hardmax = 0; + bool hardmax; if (!nname.empty() && nname[0] != '#' && source.HasPriv("memoserv/info")) { @@ -38,7 +38,7 @@ class CommandMSInfo : public Command return; } mi = &na->nc->memos; - hardmax = na->nc->HasExt("MEMO_HARDMAX") ? 1 : 0; + hardmax = na->nc->HasExt("MEMO_HARDMAX"); } else if (!nname.empty() && nname[0] == '#') { @@ -54,7 +54,7 @@ class CommandMSInfo : public Command return; } mi = &ci->memos; - hardmax = ci->HasExt("MEMO_HARDMAX") ? 1 : 0; + hardmax = ci->HasExt("MEMO_HARDMAX"); } else if (!nname.empty()) /* It's not a chan and we aren't services admin */ { @@ -64,7 +64,7 @@ class CommandMSInfo : public Command else { mi = &nc->memos; - hardmax = nc->HasExt("MEMO_HARDMAX") ? 1 : 0; + hardmax = nc->HasExt("MEMO_HARDMAX"); } if (!nname.empty() && (ci || na->nc != nc)) @@ -178,7 +178,6 @@ class CommandMSInfo : public Command else source.Reply(_("You will not be notified of new memos.")); } - return; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override diff --git a/modules/commands/ms_set.cpp b/modules/commands/ms_set.cpp index 9bf63b15d..e6d48d08a 100644 --- a/modules/commands/ms_set.cpp +++ b/modules/commands/ms_set.cpp @@ -25,27 +25,27 @@ class CommandMSSet : public Command if (param.equals_ci("ON")) { - nc->ExtendMetadata("MEMO_SIGNON"); - nc->ExtendMetadata("MEMO_RECEIVE"); + nc->Extend<bool>("MEMO_SIGNON"); + nc->Extend<bool>("MEMO_RECEIVE"); source.Reply(_("%s will now notify you of memos when you log on and when they are sent to you."), MemoServ->nick.c_str()); } else if (param.equals_ci("LOGON")) { - nc->ExtendMetadata("MEMO_SIGNON"); - nc->Shrink("MEMO_RECEIVE"); + nc->Extend<bool>("MEMO_SIGNON"); + nc->Shrink<bool>("MEMO_RECEIVE"); source.Reply(_("%s will now notify you of memos when you log on or unset /AWAY."), MemoServ->nick.c_str()); } else if (param.equals_ci("NEW")) { - nc->Shrink("MEMO_SIGNON"); - nc->ExtendMetadata("MEMO_RECEIVE"); + nc->Shrink<bool>("MEMO_SIGNON"); + nc->Extend<bool>("MEMO_RECEIVE"); source.Reply(_("%s will now notify you of memos when they are sent to you."), MemoServ->nick.c_str()); } else if (param.equals_ci("MAIL")) { if (!nc->email.empty()) { - nc->ExtendMetadata("MEMO_MAIL"); + nc->Extend<bool>("MEMO_MAIL"); source.Reply(_("You will now be informed about new memos via email.")); } else @@ -53,20 +53,18 @@ class CommandMSSet : public Command } else if (param.equals_ci("NOMAIL")) { - nc->Shrink("MEMO_MAIL"); + nc->Shrink<bool>("MEMO_MAIL"); source.Reply(_("You will no longer be informed via email.")); } else if (param.equals_ci("OFF")) { - nc->Shrink("MEMO_SIGNON"); - nc->Shrink("MEMO_RECEIVE"); - nc->Shrink("MEMO_MAIL"); + nc->Shrink<bool>("MEMO_SIGNON"); + nc->Shrink<bool>("MEMO_RECEIVE"); + nc->Shrink<bool>("MEMO_MAIL"); source.Reply(_("%s will not send you any notification of memos."), MemoServ->nick.c_str()); } else this->OnSyntaxError(source, ""); - - return; } void DoLimit(CommandSource &source, const std::vector<Anope::string> ¶ms, MemoInfo *mi) @@ -125,16 +123,16 @@ class CommandMSSet : public Command if (!chan.empty()) { if (!p2.empty()) - ci->ExtendMetadata("MEMO_HARDMAX"); + ci->Extend<bool>("MEMO_HARDMAX"); else - ci->Shrink("MEMO_HARDMAX"); + ci->Shrink<bool>("MEMO_HARDMAX"); } else { if (!p2.empty()) - nc->ExtendMetadata("MEMO_HARDMAX"); + nc->Extend<bool>("MEMO_HARDMAX"); else - nc->Shrink("MEMO_HARDMAX"); + nc->Shrink<bool>("MEMO_HARDMAX"); } limit = -1; try @@ -301,10 +299,12 @@ class CommandMSSet : public Command class MSSet : public Module { CommandMSSet commandmsset; + PrimitiveExtensibleItem<bool> memo_signon, memo_receive, memo_mail, memo_hardmax; public: MSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandmsset(this) + commandmsset(this), memo_signon(this, "MEMO_SIGNON"), memo_receive(this, "MEMO_RECEIVE"), memo_mail(this, "MEMO_MAIL"), + memo_hardmax(this, "MEMO_HARDMAX") { } diff --git a/modules/commands/ns_ajoin.cpp b/modules/commands/ns_ajoin.cpp index 0304c7394..79d7fdf4a 100644 --- a/modules/commands/ns_ajoin.cpp +++ b/modules/commands/ns_ajoin.cpp @@ -13,9 +13,9 @@ struct AJoinEntry; -struct AJoinList : Serialize::Checker<std::vector<AJoinEntry *> >, ExtensibleItem +struct AJoinList : Serialize::Checker<std::vector<AJoinEntry *> > { - AJoinList() : Serialize::Checker<std::vector<AJoinEntry *> >("AJoinEntry") { } + AJoinList(Extensible *) : Serialize::Checker<std::vector<AJoinEntry *> >("AJoinEntry") { } ~AJoinList(); }; @@ -25,7 +25,7 @@ struct AJoinEntry : Serializable Anope::string channel; Anope::string key; - AJoinEntry() : Serializable("AJoinEntry") { } + AJoinEntry(Extensible *) : Serializable("AJoinEntry") { } void Serialize(Serialize::Data &sd) const anope_override { @@ -52,7 +52,7 @@ struct AJoinEntry : Serializable aj = anope_dynamic_static_cast<AJoinEntry *>(obj); else { - aj = new AJoinEntry(); + aj = new AJoinEntry(nc); aj->owner = nc; } @@ -61,12 +61,7 @@ struct AJoinEntry : Serializable if (!obj) { - AJoinList *channels = nc->GetExt<AJoinList *>("ns_ajoin_channels"); - if (channels == NULL) - { - channels = new AJoinList(); - nc->Extend("ns_ajoin_channels", channels); - } + AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); (*channels)->push_back(aj); } @@ -84,12 +79,7 @@ class CommandNSAJoin : public Command { void DoList(CommandSource &source, NickCore *nc) { - AJoinList *channels = nc->GetExt<AJoinList *>("ns_ajoin_channels"); - if (channels == NULL) - { - channels = new AJoinList(); - nc->Extend("ns_ajoin_channels", channels); - } + AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); if ((*channels)->empty()) source.Reply(_("%s's auto join list is empty."), nc->display.c_str()); @@ -119,12 +109,7 @@ class CommandNSAJoin : public Command void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &chan, const Anope::string &key) { - AJoinList *channels = nc->GetExt<AJoinList *>("ns_ajoin_channels"); - if (channels == NULL) - { - channels = new AJoinList(); - nc->Extend("ns_ajoin_channels", channels); - } + AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); unsigned i = 0; for (; i < (*channels)->size(); ++i) @@ -139,7 +124,7 @@ class CommandNSAJoin : public Command source.Reply(CHAN_X_INVALID, chan.c_str()); else { - AJoinEntry *entry = new AJoinEntry(); + AJoinEntry *entry = new AJoinEntry(nc); entry->owner = nc; entry->channel = chan; entry->key = key; @@ -150,12 +135,7 @@ class CommandNSAJoin : public Command void DoDel(CommandSource &source, NickCore *nc, const Anope::string &chan) { - AJoinList *channels = nc->GetExt<AJoinList *>("ns_ajoin_channels"); - if (channels == NULL) - { - channels = new AJoinList(); - nc->Extend("ns_ajoin_channels", channels); - } + AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); unsigned i = 0; for (; i < (*channels)->size(); ++i) @@ -170,6 +150,9 @@ class CommandNSAJoin : public Command (*channels)->erase((*channels)->begin() + i); source.Reply(_("%s was removed from %s's auto join list."), chan.c_str(), nc->display.c_str()); } + + if ((*channels)->empty()) + nc->Shrink<AJoinList>("ajoinlist"); } public: @@ -233,10 +216,11 @@ class NSAJoin : public Module { Serialize::Type ajoinentry_type; CommandNSAJoin commandnsajoin; + ExtensibleItem<AJoinList> ajoinlist; public: NSAJoin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - ajoinentry_type("AJoinEntry", AJoinEntry::Unserialize), commandnsajoin(this) + ajoinentry_type("AJoinEntry", AJoinEntry::Unserialize), commandnsajoin(this), ajoinlist(this, "ajoinlist") { if (!IRCD->CanSVSJoin) @@ -250,12 +234,9 @@ class NSAJoin : public Module if (!NickServ) return; - AJoinList *channels = u->Account()->GetExt<AJoinList *>("ns_ajoin_channels"); + AJoinList *channels = u->Account()->GetExt<AJoinList>("ajoinlist"); if (channels == NULL) - { - channels = new AJoinList(); - u->Account()->Extend("ns_ajoin_channels", channels); - } + channels = u->Account()->Extend<AJoinList>("ajoinlist"); for (unsigned i = 0; i < (*channels)->size(); ++i) { @@ -284,7 +265,7 @@ class NSAJoin : public Module continue; else if (c->HasMode("ADMINONLY") && !u->HasMode("ADMIN")) continue; - else if (c->HasMode("SSL") && !(u->HasMode("SSL") || u->HasExt("SSL"))) + else if (c->HasMode("SSL") && !(u->HasMode("SSL") || u->HasExt("ssl"))) continue; else if (c->MatchesList(u, "BAN") == true && c->MatchesList(u, "EXCEPT") == false) need_invite = true; diff --git a/modules/commands/ns_alist.cpp b/modules/commands/ns_alist.cpp index 591af44c3..2d7b02cfd 100644 --- a/modules/commands/ns_alist.cpp +++ b/modules/commands/ns_alist.cpp @@ -62,7 +62,7 @@ class CommandNSAList : public Command { ++chan_count; entry["Number"] = stringify(chan_count); - entry["Channel"] = (ci->HasExt("NO_EXPIRE") ? "!" : "") + ci->name; + entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name; entry["Access"] = "Founder"; list.AddEntry(entry); continue; @@ -72,7 +72,7 @@ class CommandNSAList : public Command { ++chan_count; entry["Number"] = stringify(chan_count); - entry["Channel"] = (ci->HasExt("NO_EXPIRE") ? "!" : "") + ci->name; + entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name; entry["Access"] = "Successor"; list.AddEntry(entry); continue; @@ -85,7 +85,7 @@ class CommandNSAList : public Command ++chan_count; entry["Number"] = stringify(chan_count); - entry["Channel"] = (ci->HasExt("NO_EXPIRE") ? "!" : "") + ci->name; + entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name; for (unsigned j = 0; j < access.size(); ++j) entry["Access"] = entry["Access"] + ", " + access[j]->AccessSerialize(); entry["Access"] = entry["Access"].substr(2); diff --git a/modules/commands/ns_cert.cpp b/modules/commands/ns_cert.cpp index dc877e467..c87ff7179 100644 --- a/modules/commands/ns_cert.cpp +++ b/modules/commands/ns_cert.cpp @@ -10,15 +10,135 @@ */ #include "module.h" +#include "modules/ns_cert.h" -static unsigned accessmax; +struct NSCertListImpl : NSCertList +{ + Serialize::Reference<NickCore> nc; + std::vector<Anope::string> certs; + + public: + NSCertListImpl(Extensible *obj) : nc(anope_dynamic_static_cast<NickCore *>(obj)) { } + + /** Add an entry to the nick's certificate list + * + * @param entry The fingerprint to add to the cert list + * + * Adds a new entry into the cert list. + */ + void AddCert(const Anope::string &entry) anope_override + { + this->certs.push_back(entry); + FOREACH_MOD(OnNickAddCert, (this->nc, entry)); + } + + /** Get an entry from the nick's cert list by index + * + * @param entry Index in the certificaate list vector to retrieve + * @return The fingerprint entry of the given index if within bounds, an empty string if the vector is empty or the index is out of bounds + * + * Retrieves an entry from the certificate list corresponding to the given index. + */ + Anope::string GetCert(unsigned entry) const anope_override + { + if (entry >= this->certs.size()) + return ""; + return this->certs[entry]; + } + + unsigned GetCertCount() const anope_override + { + return this->certs.size(); + } + + /** Find an entry in the nick's cert list + * + * @param entry The fingerprint to search for + * @return True if the fingerprint is found in the cert list, false otherwise + * + * Search for an fingerprint within the cert list. + */ + bool FindCert(const Anope::string &entry) const anope_override + { + return std::find(this->certs.begin(), this->certs.end(), entry) != this->certs.end(); + } + + /** Erase a fingerprint from the nick's certificate list + * + * @param entry The fingerprint to remove + * + * Removes the specified fingerprint from the cert list. + */ + void EraseCert(const Anope::string &entry) anope_override + { + std::vector<Anope::string>::iterator it = std::find(this->certs.begin(), this->certs.end(), entry); + if (it != this->certs.end()) + { + FOREACH_MOD(OnNickEraseCert, (this->nc, entry)); + this->certs.erase(it); + } + } + + /** Clears the entire nick's cert list + * + * Deletes all the memory allocated in the certificate list vector and then clears the vector. + */ + void ClearCert() anope_override + { + FOREACH_MOD(OnNickClearCert, (this->nc)); + this->certs.clear(); + } + + void Check() anope_override + { + if (this->certs.empty()) + nc->Shrink<NSCertList>("certificates"); + } + + struct ExtensibleItem : ::ExtensibleItem<NSCertListImpl> + { + ExtensibleItem(Module *m, const Anope::string &name) : ::ExtensibleItem<NSCertListImpl>(m, name) { } + + void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override + { + if (s->GetSerializableType()->GetName() != "NickCore") + return; + + const NickCore *nc = anope_dynamic_static_cast<const NickCore *>(e); + NSCertList *certs = this->Get(nc); + if (certs == NULL || !certs->GetCertCount()) + return; + + for (unsigned i = 0; i < certs->GetCertCount(); ++i) + data["cert"] << certs->GetCert(i) << " "; + } + + void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override + { + if (s->GetSerializableType()->GetName() != "NickCore") + return; + + NickCore *nc = anope_dynamic_static_cast<NickCore *>(e); + NSCertListImpl *certs = this->Require(nc); + + Anope::string buf; + data["cert"] >> buf; + spacesepstream sep(buf); + certs->certs.clear(); + while (sep.GetToken(buf)) + certs->certs.push_back(buf); + } + }; +}; class CommandNSCert : public Command { private: void DoServAdminList(CommandSource &source, const NickCore *nc) { - if (nc->cert.empty()) + NSCertList *cl = nc->GetExt<NSCertList>("certificates"); + + if (!cl || !cl->GetCertCount()) { source.Reply(_("Certificate list for \002%s\002 is empty."), nc->display.c_str()); return; @@ -33,9 +153,9 @@ class CommandNSCert : public Command ListFormatter list; list.AddColumn("Certificate"); - for (unsigned i = 0, end = nc->cert.size(); i < end; ++i) + for (unsigned i = 0; i < cl->GetCertCount(); ++i) { - Anope::string fingerprint = nc->GetCert(i); + const Anope::string &fingerprint = cl->GetCert(i); ListFormatter::ListEntry entry; entry["Certificate"] = fingerprint; list.AddEntry(entry); @@ -47,74 +167,74 @@ class CommandNSCert : public Command list.Process(replies); for (unsigned i = 0; i < replies.size(); ++i) source.Reply(replies[i]); - - return; } void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &mask) { + NSCertList *cl = nc->Require<NSCertList>("certificates"); - if (nc->cert.size() >= Config->GetModule(this->owner)->Get<unsigned>("accessmax")) + if (cl->GetCertCount() >= Config->GetModule(this->owner)->Get<unsigned>("accessmax")) { source.Reply(_("Sorry, you can only have %d certificate entries for a nickname."), Config->GetModule(this->owner)->Get<unsigned>("accessmax")); return; } - if (source.GetUser() && !source.GetUser()->fingerprint.empty() && !nc->FindCert(source.GetUser()->fingerprint)) - { - nc->AddCert(source.GetUser()->fingerprint); - source.Reply(_("\002%s\002 added to your certificate list."), source.GetUser()->fingerprint.c_str()); - return; - } - if (mask.empty()) { - this->OnSyntaxError(source, "ADD"); + if (source.GetUser() && !source.GetUser()->fingerprint.empty() && !cl->FindCert(source.GetUser()->fingerprint)) + { + cl->AddCert(source.GetUser()->fingerprint); + source.Reply(_("\002%s\002 added to your certificate list."), source.GetUser()->fingerprint.c_str()); + } + else + this->OnSyntaxError(source, "ADD"); + return; } - if (nc->FindCert(mask)) + if (cl->FindCert(mask)) { source.Reply(_("Fingerprint \002%s\002 already present on your certificate list."), mask.c_str()); return; } - nc->AddCert(mask); + cl->AddCert(mask); source.Reply(_("\002%s\002 added to your certificate list."), mask.c_str()); - return; } void DoDel(CommandSource &source, NickCore *nc, const Anope::string &mask) { - if (source.GetUser() && !source.GetUser()->fingerprint.empty() && nc->FindCert(source.GetUser()->fingerprint)) - { - nc->EraseCert(source.GetUser()->fingerprint); - source.Reply(_("\002%s\002 deleted from your certificate list."), source.GetUser()->fingerprint.c_str()); - return; - } + NSCertList *cl = nc->Require<NSCertList>("certificates"); if (mask.empty()) { - this->OnSyntaxError(source, "DEL"); + if (source.GetUser() && !source.GetUser()->fingerprint.empty() && cl->FindCert(source.GetUser()->fingerprint)) + { + cl->EraseCert(source.GetUser()->fingerprint); + source.Reply(_("\002%s\002 deleted from your certificate list."), source.GetUser()->fingerprint.c_str()); + } + else + this->OnSyntaxError(source, "DEL"); + return; } - if (!nc->FindCert(mask)) + if (!cl->FindCert(mask)) { source.Reply(_("\002%s\002 not found on your certificate list."), mask.c_str()); return; } source.Reply(_("\002%s\002 deleted from your certificate list."), mask.c_str()); - nc->EraseCert(mask); - - return; + cl->EraseCert(mask); + cl->Check(); } void DoList(CommandSource &source, const NickCore *nc) { + NSCertList *cl = nc->GetExt<NSCertList>("certificates"); - if (nc->cert.empty()) + if (!cl || !cl->GetCertCount()) { source.Reply(_("Your certificate list is empty.")); return; @@ -123,10 +243,10 @@ class CommandNSCert : public Command ListFormatter list; list.AddColumn("Certificate"); - for (unsigned i = 0, end = nc->cert.size(); i < end; ++i) + for (unsigned i = 0; i < cl->GetCertCount(); ++i) { ListFormatter::ListEntry entry; - entry["Certificate"] = nc->GetCert(i); + entry["Certificate"] = cl->GetCert(i); list.AddEntry(entry); } @@ -197,8 +317,17 @@ class CommandNSCert : public Command class NSCert : public Module { CommandNSCert commandnscert; + NSCertListImpl::ExtensibleItem certs; + + public: + NSCert(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), + commandnscert(this), certs(this, "certificates") + { + if (!IRCD || !IRCD->CanCertFP) + throw ModuleException("Your IRCd does not support ssl client certificates"); + } - void DoAutoIdentify(User *u) + void OnFingerprint(User *u) anope_override { NickAlias *na = NickAlias::Find(u->nick); BotInfo *NickServ = Config->GetClient("NickServ"); @@ -208,29 +337,28 @@ class NSCert : public Module return; if (na->nc->HasExt("SUSPENDED")) return; - if (!na->nc->FindCert(u->fingerprint)) + + NSCertList *cl = certs.Get(na->nc); + if (!cl || !cl->FindCert(u->fingerprint)) return; u->Identify(na); u->SendMessage(NickServ, _("SSL Fingerprint accepted. You are now identified.")); - Log(u) << "automatically identified for account " << na->nc->display << " using a valid SSL fingerprint"; - return; + Log(u) << "automatically identified for account " << na->nc->display << " via SSL certificate fingerprint"; } - public: - NSCert(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandnscert(this) + EventReturn OnNickValidate(User *u, NickAlias *na) anope_override { + NSCertList *cl = certs.Get(na->nc); + if (!u->fingerprint.empty() && cl && cl->FindCert(u->fingerprint)) + { + u->Identify(na); + u->SendMessage(Config->GetClient("NickServ"), _("SSL fingerprint accepted, you are now identified.")); + Log(u) << "automatically identified for account " << na->nc->display << " via SSL fingerprint."; + return EVENT_ALLOW; + } - if (!IRCD || !IRCD->CanCertFP) - throw ModuleException("Your IRCd does not support ssl client certificates"); - - - } - - void OnFingerprint(User *u) anope_override - { - DoAutoIdentify(u); + return EVENT_CONTINUE; } }; diff --git a/modules/commands/ns_group.cpp b/modules/commands/ns_group.cpp index 989fc1385..160980bc9 100644 --- a/modules/commands/ns_group.cpp +++ b/modules/commands/ns_group.cpp @@ -10,6 +10,7 @@ */ #include "module.h" +#include "modules/ns_cert.h" class NSGroupRequest : public IdentifyRequest { @@ -43,7 +44,6 @@ class NSGroupRequest : public IdentifyRequest na->time_registered = na->last_seen = Anope::CurTime; u->Login(target->nc); - IRCD->SendLogin(u); FOREACH_MOD(OnNickGroup, (u, target)); Log(LOG_COMMAND, source, cmd) << "makes " << nick << " join group of " << target->nick << " (" << target->nc->display << ") (email: " << (!target->nc->email.empty() ? target->nc->email : "none") << ")"; @@ -141,7 +141,9 @@ class CommandNSGroup : public Command bool ok = false; if (!na && u->Account()) ok = true; - else if (!u->fingerprint.empty() && target->nc->FindCert(u->fingerprint)) + + NSCertList *cl = target->nc->GetExt<NSCertList>("certificates"); + if (!u->fingerprint.empty() && cl && cl->FindCert(u->fingerprint)) ok = true; if (ok == false && !pass.empty()) @@ -236,8 +238,6 @@ class CommandNSUngroup : public Command nc->pass = oldcore->pass; if (!oldcore->email.empty()) nc->email = oldcore->email; - if (!oldcore->greet.empty()) - nc->greet = oldcore->greet; nc->language = oldcore->language; source.Reply(_("Nick %s has been ungrouped from %s."), na->nick.c_str(), oldcore->display.c_str()); @@ -303,7 +303,7 @@ class CommandNSGList : public Command ListFormatter::ListEntry entry; entry["Nick"] = na2->nick; - entry["Expires"] = (na2->HasExt("NO_EXPIRE") || !nickserv_expire || Anope::NoExpire) ? "Does not expire" : ("expires in " + Anope::strftime(na2->last_seen + nickserv_expire)); + entry["Expires"] = (na2->HasExt("NS_NO_EXPIRE") || !nickserv_expire || Anope::NoExpire) ? "Does not expire" : ("expires in " + Anope::strftime(na2->last_seen + nickserv_expire)); list.AddEntry(entry); } diff --git a/modules/commands/ns_info.cpp b/modules/commands/ns_info.cpp index 864136cd8..8fc5aa639 100644 --- a/modules/commands/ns_info.cpp +++ b/modules/commands/ns_info.cpp @@ -13,17 +13,6 @@ class CommandNSInfo : public Command { - private: - void CheckOptStr(NickCore *core, Anope::string &buf, const Anope::string &opt, const char *str, const Extensible *e, bool reverse_logic = false) - { - if (reverse_logic != e->HasExt(opt)) - { - if (!buf.empty()) - buf += ", "; - - buf += Language::Translate(core, str); - } - } public: CommandNSInfo(Module *creator) : Command(creator, "nickserv/info", 0, 2) { @@ -105,34 +94,6 @@ class CommandNSInfo : public Command else info[_("VHost")] = na->GetVhostHost(); } - - if (!na->nc->greet.empty()) - info[_("Greet")] = na->nc->greet; - - Anope::string optbuf; - - CheckOptStr(source.nc, optbuf, "KILLPROTECT", _("Protection"), na->nc); - CheckOptStr(source.nc, optbuf, "SECURE", _("Security"), na->nc); - CheckOptStr(source.nc, optbuf, "PRIVATE", _("Private"), na->nc); - CheckOptStr(source.nc, optbuf, "MSG", _("Message mode"), na->nc); - CheckOptStr(source.nc, optbuf, "AUTOOP", _("Auto-op"), na->nc); - CheckOptStr(source.nc, optbuf, "SUSPENDED", _("Suspended"), na->nc); - CheckOptStr(source.nc, optbuf, "STATS", _("Chanstats"), na->nc); - CheckOptStr(source.nc, optbuf, "NO_EXPIRE", _("No expire"), na); - - info[_("Options")] = optbuf.empty() ? _("None") : optbuf; - - if (na->nc->HasExt("UNCONFIRMED") == false) - { - time_t nickserv_expire = Config->GetModule("nickserv")->Get<time_t>("expire"); - if (!na->HasExt("NO_EXPIRE") && nickserv_expire && !Anope::NoExpire) - info[_("Expires")] = Anope::strftime(na->last_seen + nickserv_expire); - } - else - { - time_t unconfirmed_expire = Config->GetModule("nickserv")->Get<time_t>("unconfirmedexpire", "1d"); - info[_("Expires")] = Anope::strftime(na->time_registered + unconfirmed_expire); - } } FOREACH_MOD(OnNickInfo, (source, na, info, show_hidden)); @@ -159,13 +120,142 @@ class CommandNSInfo : public Command } }; + +class CommandNSSetHide : public Command +{ + public: + CommandNSSetHide(Module *creator, const Anope::string &sname = "nickserv/set/hide", size_t min = 2) : Command(creator, sname, min, min + 1) + { + this->SetDesc(_("Hide certain pieces of nickname information")); + this->SetSyntax(_("{EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}")); + } + + void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m, const Anope::string &arg) + { + const NickAlias *na = NickAlias::Find(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param)); + if (MOD_RESULT == EVENT_STOP) + return; + + Anope::string onmsg, offmsg, flag; + + if (param.equals_ci("EMAIL")) + { + flag = "HIDE_EMAIL"; + onmsg = _("The E-mail address of \002%s\002 will now be hidden from %s INFO displays."); + offmsg = _("The E-mail address of \002%s\002 will now be shown in %s INFO displays."); + } + else if (param.equals_ci("USERMASK")) + { + flag = "HIDE_MASK"; + onmsg = _("The last seen user@host mask of \002%s\002 will now be hidden from %s INFO displays."); + offmsg = _("The last seen user@host mask of \002%s\002 will now be shown in %s INFO displays."); + } + else if (param.equals_ci("STATUS")) + { + flag = "HIDE_STATUS"; + onmsg = _("The services access status of \002%s\002 will now be hidden from %s INFO displays."); + offmsg = _("The services access status of \002%s\002 will now be shown in %s INFO displays."); + } + else if (param.equals_ci("QUIT")) + { + flag = "HIDE_QUIT"; + onmsg = _("The last quit message of \002%s\002 will now be hidden from %s INFO displays."); + offmsg = _("The last quit message of \002%s\002 will now be shown in %s INFO displays."); + } + else + { + this->OnSyntaxError(source, "HIDE"); + return; + } + + if (arg.equals_ci("ON")) + { + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param << " to " << arg << " for " << nc->display; + nc->Extend<bool>(flag); + source.Reply(onmsg.c_str(), nc->display.c_str(), source.service->nick.c_str()); + } + else if (arg.equals_ci("OFF")) + { + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param << " to " << arg << " for " << nc->display; + nc->Shrink<bool>(flag); + source.Reply(offmsg.c_str(), nc->display.c_str(), source.service->nick.c_str()); + } + else + this->OnSyntaxError(source, "HIDE"); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + this->Run(source, source.nc->display, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows you to prevent certain pieces of information from\n" + "being displayed when someone does a %s \002INFO\002 on your\n" + "nick. You can hide your E-mail address (\002EMAIL\002), last seen\n" + "user@host mask (\002USERMASK\002), your services access status\n" + "(\002STATUS\002) and last quit message (\002QUIT\002).\n" + "The second parameter specifies whether the information should\n" + "be displayed (\002OFF\002) or hidden (\002ON\002)."), source.service->nick.c_str()); + return true; + } +}; + +class CommandNSSASetHide : public CommandNSSetHide +{ + public: + CommandNSSASetHide(Module *creator) : CommandNSSetHide(creator, "nickserv/saset/hide", 3) + { + this->SetSyntax("\037nickname\037 {EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}"); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + this->ClearSyntax(); + this->Run(source, params[0], params[1], params[2]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Allows you to prevent certain pieces of information from\n" + "being displayed when someone does a %s \002INFO\002 on the\n" + "nick. You can hide the E-mail address (\002EMAIL\002), last seen\n" + "user@host mask (\002USERMASK\002), the services access status\n" + "(\002STATUS\002) and last quit message (\002QUIT\002).\n" + "The second parameter specifies whether the information should\n" + "be displayed (\002OFF\002) or hidden (\002ON\002)."), source.service->nick.c_str()); + return true; + } +}; + class NSInfo : public Module { CommandNSInfo commandnsinfo; + CommandNSSetHide commandnssethide; + CommandNSSASetHide commandnssasethide; + + SerializableExtensibleItem<bool> hide_email, hide_usermask, hide_status, hide_quit; + public: NSInfo(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandnsinfo(this) + commandnsinfo(this), commandnssethide(this), commandnssasethide(this), + hide_email(this, "HIDE_EMAIL"), hide_usermask(this, "HIDE_MASK"), hide_status(this, "HIDE_STATUS"), + hide_quit(this, "HIDE_QUIT") { } diff --git a/modules/commands/ns_list.cpp b/modules/commands/ns_list.cpp index a4e73e857..b1ab19971 100644 --- a/modules/commands/ns_list.cpp +++ b/modules/commands/ns_list.cpp @@ -83,9 +83,9 @@ class CommandNSList : public Command const NickAlias *na = it->second; /* Don't show private nicks to non-services admins. */ - if (na->nc->HasExt("PRIVATE") && !is_servadmin && na->nc != mync) + if (na->nc->HasExt("NS_PRIVATE") && !is_servadmin && na->nc != mync) continue; - else if (nsnoexpire && !na->HasExt("NO_EXPIRE")) + else if (nsnoexpire && !na->HasExt("NS_NO_EXPIRE")) continue; else if (suspended && !na->nc->HasExt("SUSPENDED")) continue; @@ -101,7 +101,7 @@ class CommandNSList : public Command if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= listmax) { bool isnoexpire = false; - if (is_servadmin && na->HasExt("NO_EXPIRE")) + if (is_servadmin && na->HasExt("NS_NO_EXPIRE")) isnoexpire = true; ListFormatter::ListEntry entry; @@ -177,14 +177,118 @@ class CommandNSList : public Command } }; + +class CommandNSSetPrivate : public Command +{ + public: + CommandNSSetPrivate(Module *creator, const Anope::string &sname = "nickserv/set/private", size_t min = 1) : Command(creator, sname, min, min + 1) + { + this->SetDesc(_("Prevent the nickname from appearing in the LIST command")); + this->SetSyntax(_("{ON | OFF}")); + } + + void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) + { + const NickAlias *na = NickAlias::Find(user); + if (!na) + { + source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); + return; + } + NickCore *nc = na->nc; + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param)); + if (MOD_RESULT == EVENT_STOP) + return; + + if (param.equals_ci("ON")) + { + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable private for " << nc->display; + nc->Extend<bool>("NS_PRIVATE"); + source.Reply(_("Private option is now \002on\002 for \002%s\002."), nc->display.c_str()); + } + else if (param.equals_ci("OFF")) + { + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable private for " << nc->display; + nc->Shrink<bool>("NS_PRIVATE"); + source.Reply(_("Private option is now \002off\002 for \002%s\002."), nc->display.c_str()); + } + else + this->OnSyntaxError(source, "PRIVATE"); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + this->Run(source, source.nc->display, params[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Turns %s's privacy option on or off for your nick.\n" + "With \002PRIVATE\002 set, your nickname will not appear in\n" + "nickname lists generated with %s's \002LIST\002 command.\n" + "(However, anyone who knows your nickname can still get\n" + "information on it using the \002INFO\002 command.)"), + source.service->nick.c_str(), source.service->nick.c_str()); + return true; + } +}; + +class CommandNSSASetPrivate : public CommandNSSetPrivate +{ + public: + CommandNSSASetPrivate(Module *creator) : CommandNSSetPrivate(creator, "nickserv/saset/private", 2) + { + this->ClearSyntax(); + this->SetSyntax(_("\037nickname\037 {ON | OFF}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override + { + this->Run(source, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) anope_override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Turns %s's privacy option on or off for the nick.\n" + "With \002PRIVATE\002 set, the nickname will not appear in\n" + "nickname lists generated with %s's \002LIST\002 command.\n" + "(However, anyone who knows the nickname can still get\n" + "information on it using the \002INFO\002 command.)"), + source.service->nick.c_str(), source.service->nick.c_str()); + return true; + } +}; + + class NSList : public Module { CommandNSList commandnslist; + CommandNSSetPrivate commandnssetprivate; + CommandNSSASetPrivate commandnssasetprivate; + + SerializableExtensibleItem<bool> priv; + public: NSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandnslist(this) + commandnslist(this), commandnssetprivate(this), commandnssasetprivate(this), + priv(this, "NS_PRIVATE") + { + } + + void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_all) anope_override { + if (!show_all) + return; + + if (priv.HasExt(na->nc)) + info.AddOption(_("Private")); } }; diff --git a/modules/commands/ns_recover.cpp b/modules/commands/ns_recover.cpp index 224b4e2ed..48c625ace 100644 --- a/modules/commands/ns_recover.cpp +++ b/modules/commands/ns_recover.cpp @@ -10,10 +10,11 @@ */ #include "module.h" +#include "modules/ns_cert.h" static ServiceReference<NickServService> nickserv("NickServService", "NickServ"); -struct NSRecoverExtensibleInfo : ExtensibleItem, std::map<Anope::string, ChannelStatus> { }; +typedef std::map<Anope::string, ChannelStatus> NSRecoverInfo; class NSRecoverRequest : public IdentifyRequest { @@ -50,7 +51,7 @@ class NSRecoverRequest : public IdentifyRequest // same person that is executing the command, so kill them off (old GHOST command). else if (u->Account() == na->nc) { - if (!source.GetAccount() && na->nc->HasExt("SECURE")) + if (!source.GetAccount() && na->nc->HasExt("NS_SECURE")) { source.GetUser()->Login(u->Account()); Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << u->Account()->display; @@ -60,11 +61,9 @@ class NSRecoverRequest : public IdentifyRequest { if (!u->chans.empty()) { - NSRecoverExtensibleInfo *ei = new NSRecoverExtensibleInfo; + NSRecoverInfo *ei = source.GetUser()->Extend<NSRecoverInfo>("recover"); for (User::ChanUserList::iterator it = u->chans.begin(), it_end = u->chans.end(); it != it_end; ++it) (*ei)[it->first->name] = it->second->status; - - source.GetUser()->Extend("ns_recover_info", ei); } } @@ -83,7 +82,7 @@ class NSRecoverRequest : public IdentifyRequest /* User is not identified or not identified to the same account as the person using this command */ else { - if (!source.GetAccount() && na->nc->HasExt("SECURE")) + if (!source.GetAccount() && na->nc->HasExt("NS_SECURE")) { source.GetUser()->Login(na->nc); // Identify the user using the command if they arent identified Log(LOG_COMMAND, source, cmd) << "and was automatically identified to " << na->nick << " (" << na->nc->display << ")"; @@ -161,9 +160,11 @@ class CommandNSRecover : public Command bool ok = false; if (source.GetAccount() == na->nc) ok = true; - else if (!na->nc->HasExt("SECURE") && source.GetUser() && na->nc->IsOnAccess(source.GetUser())) + else if (!na->nc->HasExt("NS_SECURE") && source.GetUser() && na->nc->IsOnAccess(source.GetUser())) ok = true; - else if (source.GetUser() && !source.GetUser()->fingerprint.empty() && na->nc->FindCert(source.GetUser()->fingerprint)) + + NSCertList *cl = na->nc->GetExt<NSCertList>("certificates"); + if (source.GetUser() && !source.GetUser()->fingerprint.empty() && cl && cl->FindCert(source.GetUser()->fingerprint)) ok = true; if (ok == false && !pass.empty()) @@ -200,10 +201,11 @@ class CommandNSRecover : public Command class NSRecover : public Module { CommandNSRecover commandnsrecover; + PrimitiveExtensibleItem<NSRecoverInfo> recover; public: NSRecover(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandnsrecover(this) + commandnsrecover(this), recover(this, "recover") { if (Config->GetBlock("options")->Get<bool>("nonicknameownership")) @@ -211,34 +213,15 @@ class NSRecover : public Module } - ~NSRecover() - { - for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - it->second->Shrink("ns_recover_info"); - - OnShutdown(); - } - - void OnShutdown() anope_override - { - /* On shutdown, restart, or mod unload, remove all of our holds for nicks (svshold or qlines) - * because some IRCds do not allow us to have these automatically expire - */ - for (nickalias_map::const_iterator it = NickAliasList->begin(); it != NickAliasList->end(); ++it) - nickserv->Release(it->second); - } - - void OnRestart() anope_override { OnShutdown(); } - void OnUserNickChange(User *u, const Anope::string &oldnick) anope_override { if (Config->GetModule(this)->Get<bool>("restoreonrecover")) { - NSRecoverExtensibleInfo *ei = u->GetExt<NSRecoverExtensibleInfo *>("ns_recover_info"); + NSRecoverInfo *ei = recover.Get(u); BotInfo *NickServ = Config->GetClient("NickServ"); if (ei != NULL && NickServ != NULL) - for (std::map<Anope::string, ChannelStatus>::iterator it = ei->begin(), it_end = ei->end(); it != it_end;) + for (NSRecoverInfo::iterator it = ei->begin(), it_end = ei->end(); it != it_end;) { Channel *c = Channel::Find(it->first); const Anope::string &cname = it->first; @@ -257,11 +240,11 @@ class NSRecover : public Module { if (Config->GetModule(this)->Get<bool>("restoreonrecover")) { - NSRecoverExtensibleInfo *ei = u->GetExt<NSRecoverExtensibleInfo *>("ns_recover_info"); + NSRecoverInfo *ei = recover.Get(u); if (ei != NULL) { - std::map<Anope::string, ChannelStatus>::iterator it = ei->find(c->name); + NSRecoverInfo::iterator it = ei->find(c->name); if (it != ei->end()) { for (size_t i = 0; i < it->second.Modes().length(); ++i) @@ -269,7 +252,7 @@ class NSRecover : public Module ei->erase(it); if (ei->empty()) - u->Shrink("ns_recover_info"); + recover.Unset(u); } } } diff --git a/modules/commands/ns_register.cpp b/modules/commands/ns_register.cpp index 4064ce924..e8adec549 100644 --- a/modules/commands/ns_register.cpp +++ b/modules/commands/ns_register.cpp @@ -36,27 +36,27 @@ class CommandNSConfirm : public Command source.Reply(_("Nick \002%s\002 is already confirmed."), na->nick.c_str()); else { - na->nc->Shrink("UNCONFIRMED"); + na->nc->Shrink<bool>("UNCONFIRMED"); Log(LOG_ADMIN, source, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")"; source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str()); } } else if (source.nc) { - Anope::string *code = source.nc->GetExt<ExtensibleItemClass<Anope::string> *>("ns_register_passcode"); + Anope::string *code = source.nc->GetExt<Anope::string>("passcode"); if (code != NULL && *code == passcode) { NickCore *nc = source.nc; - nc->Shrink("ns_register_passcode"); + nc->Shrink<Anope::string>("passcode"); Log(LOG_COMMAND, source, this) << "to confirm their email"; source.Reply(_("Your email address of \002%s\002 has been confirmed."), source.nc->email.c_str()); - nc->Shrink("UNCONFIRMED"); + nc->Shrink<bool>("UNCONFIRMED"); if (source.GetUser()) { IRCD->SendLogin(source.GetUser()); const NickAlias *na = NickAlias::Find(source.GetNick()); - if (!Config->GetBlock("options")->Get<bool>("nonicknameownership") && na != NULL && na->nc == source.GetAccount() && na->nc->HasExt("UNCONFIRMED") == false) + if (!Config->GetBlock("options")->Get<bool>("nonicknameownership") && na != NULL && na->nc == source.GetAccount() && !na->nc->HasExt("UNCONFIRMED")) source.GetUser()->SetMode(source.service, "REGISTERED"); } } @@ -207,12 +207,12 @@ class CommandNSRegister : public Command if (nsregister.equals_ci("admin")) { - nc->ExtendMetadata("UNCONFIRMED"); + nc->Extend<bool>("UNCONFIRMED"); source.Reply(_("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed.")); } else if (nsregister.equals_ci("mail")) { - nc->ExtendMetadata("UNCONFIRMED"); + nc->Extend<bool>("UNCONFIRMED"); if (SendRegmail(u, na, source.service)) { time_t unconfirmed_expire = Config->GetModule("nickserv")->Get<time_t>("unconfirmedexpire", "1d"); @@ -292,7 +292,7 @@ class CommandNSResend : public Command if (na == NULL) source.Reply(NICK_NOT_REGISTERED); - else if (na->nc != source.GetAccount() || source.nc->HasExt("UNCONFIRMED") == false) + else if (na->nc != source.GetAccount() || !source.nc->HasExt("UNCONFIRMED")) source.Reply(_("Your account is already confirmed.")); else { @@ -336,23 +336,55 @@ class NSRegister : public Module CommandNSConfirm commandnsconfirm; CommandNSResend commandnsrsend; + PrimitiveExtensibleItem<bool> unconfirmed; + PrimitiveExtensibleItem<Anope::string> passcode; + public: NSRegister(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandnsregister(this), commandnsconfirm(this), commandnsrsend(this) + commandnsregister(this), commandnsconfirm(this), commandnsrsend(this), unconfirmed(this, "UNCONFIRMED"), + passcode(this, "passcode") { if (Config->GetModule(this)->Get<const Anope::string>("registration").equals_ci("disable")) throw ModuleException("Module " + this->name + " will not load with registration disabled."); } + + void OnNickIdentify(User *u) anope_override + { + BotInfo *NickServ; + if (unconfirmed.HasExt(u->Account()) && (NickServ = Config->GetClient("NickServ"))) + { + const Anope::string &nsregister = Config->GetModule(this)->Get<const Anope::string>("registration"); + if (nsregister.equals_ci("admin")) + u->SendMessage(NickServ, _("All new accounts must be validated by an administrator. Please wait for your registration to be confirmed.")); + else + u->SendMessage(NickServ, _("Your email address is not confirmed. To confirm it, follow the instructions that were emailed to you when you registered.")); + const NickAlias *this_na = NickAlias::Find(u->Account()->display); + time_t time_registered = Anope::CurTime - this_na->time_registered; + time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d"); + if (unconfirmed_expire > time_registered) + u->SendMessage(NickServ, _("Your account will expire, if not confirmed, in %s"), Anope::Duration(unconfirmed_expire - time_registered).c_str()); + } + } + + void OnPreNickExpire(NickAlias *na, bool &expire) anope_override + { + if (unconfirmed.HasExt(na->nc)) + { + time_t unconfirmed_expire = Config->GetModule(this)->Get<time_t>("unconfirmedexpire", "1d"); + if (unconfirmed_expire && Anope::CurTime - na->time_registered >= unconfirmed_expire) + expire = true; + } + } }; static bool SendRegmail(User *u, const NickAlias *na, const BotInfo *bi) { NickCore *nc = na->nc; - Anope::string *code = na->nc->GetExt<ExtensibleItemClass<Anope::string> *>("ns_register_passcode"); - Anope::string codebuf; + Anope::string *code = na->nc->GetExt<Anope::string>("passcode"); if (code == NULL) { + code = na->nc->Extend<Anope::string>("passcode"); 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', @@ -362,22 +394,19 @@ static bool SendRegmail(User *u, const NickAlias *na, const BotInfo *bi) }; int idx, min = 1, max = 62; for (idx = 0; idx < 9; ++idx) - codebuf += chars[1 + static_cast<int>((static_cast<float>(max - min)) * static_cast<uint16_t>(rand()) / 65536.0) + min]; - nc->Extend("ns_register_passcode", new ExtensibleItemClass<Anope::string>(codebuf)); + *code += chars[1 + static_cast<int>((static_cast<float>(max - min)) * static_cast<uint16_t>(rand()) / 65536.0) + min]; } - else - codebuf = *code; Anope::string subject = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("registration_subject").c_str()), message = Language::Translate(na->nc, Config->GetBlock("mail")->Get<const Anope::string>("registration_message").c_str()); subject = subject.replace_all_cs("%n", na->nick); subject = subject.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname")); - subject = subject.replace_all_cs("%c", codebuf); + subject = subject.replace_all_cs("%c", *code); message = message.replace_all_cs("%n", na->nick); message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname")); - message = message.replace_all_cs("%c", codebuf); + message = message.replace_all_cs("%c", *code); return Mail::Send(u, nc, bi, subject, message); } diff --git a/modules/commands/ns_resetpass.cpp b/modules/commands/ns_resetpass.cpp index 9b15e53ac..871864b6d 100644 --- a/modules/commands/ns_resetpass.cpp +++ b/modules/commands/ns_resetpass.cpp @@ -53,7 +53,7 @@ class CommandNSResetPass : public Command } }; -struct ResetInfo : ExtensibleItem +struct ResetInfo { Anope::string code; time_t time; @@ -62,21 +62,14 @@ struct ResetInfo : ExtensibleItem class NSResetPass : public Module { CommandNSResetPass commandnsresetpass; + PrimitiveExtensibleItem<ResetInfo> reset; public: NSResetPass(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandnsresetpass(this) + commandnsresetpass(this), reset(this, "reset") { if (!Config->GetBlock("mail")->Get<bool>("usemail")) throw ModuleException("Not using mail."); - - - } - - ~NSResetPass() - { - for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it) - it->second->Shrink("ns_resetpass"); } EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> ¶ms) anope_override @@ -85,24 +78,23 @@ class NSResetPass : public Module { NickAlias *na = NickAlias::Find(params[0]); - ResetInfo *ri = na ? na->nc->GetExt<ResetInfo *>("ns_resetpass") : NULL; + ResetInfo *ri = na ? reset.Get(na->nc) : NULL; if (na && ri) { NickCore *nc = na->nc; const Anope::string &passcode = params[1]; if (ri->time < Anope::CurTime - 3600) { - nc->Shrink("ns_resetpass"); + reset.Unset(nc); source.Reply(_("Your password reset request has expired.")); } else if (passcode.equals_cs(ri->code)) { - nc->Shrink("ns_resetpass"); + reset.Unset(nc); + nc->Shrink<bool>("UNCONFIRMED"); Log(LOG_COMMAND, source, &commandnsresetpass) << "confirmed RESETPASS to forcefully identify as " << na->nick; - nc->Shrink("UNCONFIRMED"); - if (source.GetUser()) { source.GetUser()->Identify(na); @@ -147,13 +139,11 @@ static bool SendResetEmail(User *u, const NickAlias *na, const BotInfo *bi) message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname")); message = message.replace_all_cs("%c", passcode); - ResetInfo *ri = new ResetInfo; + ResetInfo *ri = na->nc->Extend<ResetInfo>("reset"); ri->code = passcode; ri->time = Anope::CurTime; - NickCore *nc = na->nc; - nc->Extend("ns_resetpass", ri); - return Mail::Send(u, nc, bi, subject, message); + return Mail::Send(u, na->nc, bi, subject, message); } MODULE_INIT(NSResetPass) diff --git a/modules/commands/ns_set.cpp b/modules/commands/ns_set.cpp index 7b0de5565..08cf3966a 100644 --- a/modules/commands/ns_set.cpp +++ b/modules/commands/ns_set.cpp @@ -191,8 +191,6 @@ class CommandNSSASetPassword : public Command source.Reply(_("Password for \002%s\002 changed to \002%s\002."), nc->display.c_str(), tmp_pass.c_str()); else source.Reply(_("Password for \002%s\002 changed."), nc->display.c_str()); - - return; } bool OnHelp(CommandSource &source, const Anope::string &) anope_override @@ -231,13 +229,13 @@ class CommandNSSetAutoOp : public Command if (param.equals_ci("ON")) { Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable autoop for " << na->nc->display; - nc->ExtendMetadata("AUTOOP"); + nc->Extend<bool>("AUTOOP"); source.Reply(_("Services will from now on set status modes on %s in channels."), nc->display.c_str()); } else if (param.equals_ci("OFF")) { Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable autoop for " << na->nc->display; - nc->Shrink("AUTOOP"); + nc->Shrink<bool>("AUTOOP"); source.Reply(_("Services will no longer set status modes on %s in channels."), nc->display.c_str()); } else @@ -313,19 +311,17 @@ class CommandNSSetChanstats : public Command if (param.equals_ci("ON")) { Log(na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable chanstats for " << na->nc->display; - na->nc->ExtendMetadata("STATS"); + na->nc->Extend<bool>("NS_STATS"); source.Reply(_("Chanstat statistics are now enabled for your nick.")); } else if (param.equals_ci("OFF")) { Log(na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable chanstats for " << na->nc->display; - na->nc->Shrink("STATS"); + na->nc->Shrink<bool>("NS_STATS"); source.Reply(_("Chanstat statistics are now disabled for your nick.")); } else this->OnSyntaxError(source, "CHANSTATS"); - - return; } void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override @@ -446,7 +442,7 @@ class CommandNSSASetDisplay : public CommandNSSetDisplay class CommandNSSetEmail : public Command { - static bool SendConfirmMail(User *u, const BotInfo *bi) + static bool SendConfirmMail(User *u, const BotInfo *bi, const Anope::string &new_email) { int chars[] = { ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', @@ -460,7 +456,9 @@ class CommandNSSetEmail : public Command for (idx = 0; idx < 9; ++idx) code += chars[1 + static_cast<int>((static_cast<float>(max - min)) * static_cast<uint16_t>(rand()) / 65536.0) + min]; - u->Account()->Extend("ns_set_email_passcode", new ExtensibleItemClass<Anope::string>(code)); + std::pair<Anope::string, Anope::string> *n = u->Account()->Extend<std::pair<Anope::string, Anope::string> >("ns_set_email"); + n->first = new_email; + n->second = code; Anope::string subject = Config->GetBlock("mail")->Get<const Anope::string>("emailchange_subject"), message = Config->GetBlock("mail")->Get<const Anope::string>("emailchange_message"); @@ -473,7 +471,11 @@ class CommandNSSetEmail : public Command message = message.replace_all_cs("%N", Config->GetBlock("networkinfo")->Get<const Anope::string>("networkname")); message = message.replace_all_cs("%c", code); - return Mail::Send(u, u->Account(), bi, subject, message); + Anope::string old = u->Account()->email; + u->Account()->email = new_email; + bool b = Mail::Send(u, u->Account(), bi, subject, message); + u->Account()->email = old; + return b; } public: @@ -516,12 +518,8 @@ class CommandNSSetEmail : public Command if (!param.empty() && Config->GetModule("nickserv")->Get<bool>("forceemail", "yes") && !source.IsServicesOper()) { - source.nc->Extend("ns_set_email", new ExtensibleItemClass<Anope::string>(param)); - Anope::string old = source.nc->email; - source.nc->email = param; - if (SendConfirmMail(source.GetUser(), source.service)) + if (SendConfirmMail(source.GetUser(), source.service, param)) source.Reply(_("A confirmation e-mail has been sent to \002%s\002. Follow the instructions in it to change your e-mail address."), param.c_str()); - source.nc->email = old; } else { @@ -579,212 +577,6 @@ class CommandNSSASetEmail : public CommandNSSetEmail } }; -class CommandNSSetGreet : public Command -{ - public: - CommandNSSetGreet(Module *creator, const Anope::string &sname = "nickserv/set/greet", size_t min = 0) : Command(creator, sname, min, min + 1) - { - this->SetDesc(_("Associate a greet message with your nickname")); - this->SetSyntax(_("\037message\037")); - } - - void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) - { - const NickAlias *na = NickAlias::Find(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - EventReturn MOD_RESULT; - FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param)); - if (MOD_RESULT == EVENT_STOP) - return; - - if (!param.empty()) - { - Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the greet of " << nc->display; - nc->greet = param; - source.Reply(_("Greet message for \002%s\002 changed to \002%s\002."), nc->display.c_str(), nc->greet.c_str()); - } - else - { - Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to unset the greet of " << nc->display; - nc->greet.clear(); - source.Reply(_("Greet message for \002%s\002 unset."), nc->display.c_str()); - } - - return; - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - this->Run(source, source.nc->display, params.size() > 0 ? params[0] : ""); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Makes the given message the greet of your nickname, that\n" - "will be displayed when joining a channel that has GREET\n" - "option enabled, provided that you have the necessary\n" - "access on it.")); - return true; - } -}; - -class CommandNSSASetGreet : public CommandNSSetGreet -{ - public: - CommandNSSASetGreet(Module *creator) : CommandNSSetGreet(creator, "nickserv/saset/greet", 1) - { - this->ClearSyntax(); - this->SetSyntax(_("\037nickname\037 \037message\037")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - this->Run(source, params[0], params.size() > 1 ? params[1] : ""); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Makes the given message the greet of the nickname, that\n" - "will be displayed when joining a channel that has GREET\n" - "option enabled, provided that the user has the necessary\n" - "access on it.")); - return true; - } -}; - -class CommandNSSetHide : public Command -{ - public: - CommandNSSetHide(Module *creator, const Anope::string &sname = "nickserv/set/hide", size_t min = 2) : Command(creator, sname, min, min + 1) - { - this->SetDesc(_("Hide certain pieces of nickname information")); - this->SetSyntax(_("{EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}")); - } - - void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m, const Anope::string &arg) - { - const NickAlias *na = NickAlias::Find(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - EventReturn MOD_RESULT; - FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param)); - if (MOD_RESULT == EVENT_STOP) - return; - - Anope::string onmsg, offmsg, flag; - - if (param.equals_ci("EMAIL")) - { - flag = "HIDE_EMAIL"; - onmsg = _("The E-mail address of \002%s\002 will now be hidden from %s INFO displays."); - offmsg = _("The E-mail address of \002%s\002 will now be shown in %s INFO displays."); - } - else if (param.equals_ci("USERMASK")) - { - flag = "HIDE_MASK"; - onmsg = _("The last seen user@host mask of \002%s\002 will now be hidden from %s INFO displays."); - offmsg = _("The last seen user@host mask of \002%s\002 will now be shown in %s INFO displays."); - } - else if (param.equals_ci("STATUS")) - { - flag = "HIDE_STATUS"; - onmsg = _("The services access status of \002%s\002 will now be hidden from %s INFO displays."); - offmsg = _("The services access status of \002%s\002 will now be shown in %s INFO displays."); - } - else if (param.equals_ci("QUIT")) - { - flag = "HIDE_QUIT"; - onmsg = _("The last quit message of \002%s\002 will now be hidden from %s INFO displays."); - offmsg = _("The last quit message of \002%s\002 will now be shown in %s INFO displays."); - } - else - { - this->OnSyntaxError(source, "HIDE"); - return; - } - - if (arg.equals_ci("ON")) - { - Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param << " to " << arg << " for " << nc->display; - nc->ExtendMetadata(flag); - source.Reply(onmsg.c_str(), nc->display.c_str(), source.service->nick.c_str()); - } - else if (arg.equals_ci("OFF")) - { - Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change hide " << param << " to " << arg << " for " << nc->display; - nc->Shrink(flag); - source.Reply(offmsg.c_str(), nc->display.c_str(), source.service->nick.c_str()); - } - else - this->OnSyntaxError(source, "HIDE"); - - return; - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - this->Run(source, source.nc->display, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows you to prevent certain pieces of information from\n" - "being displayed when someone does a %s \002INFO\002 on your\n" - "nick. You can hide your E-mail address (\002EMAIL\002), last seen\n" - "user@host mask (\002USERMASK\002), your services access status\n" - "(\002STATUS\002) and last quit message (\002QUIT\002).\n" - "The second parameter specifies whether the information should\n" - "be displayed (\002OFF\002) or hidden (\002ON\002)."), source.service->nick.c_str()); - return true; - } -}; - -class CommandNSSASetHide : public CommandNSSetHide -{ - public: - CommandNSSASetHide(Module *creator) : CommandNSSetHide(creator, "nickserv/saset/hide", 3) - { - this->SetSyntax("\037nickname\037 {EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}"); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - this->ClearSyntax(); - this->Run(source, params[0], params[1], params[2]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Allows you to prevent certain pieces of information from\n" - "being displayed when someone does a %s \002INFO\002 on the\n" - "nick. You can hide the E-mail address (\002EMAIL\002), last seen\n" - "user@host mask (\002USERMASK\002), the services access status\n" - "(\002STATUS\002) and last quit message (\002QUIT\002).\n" - "The second parameter specifies whether the information should\n" - "be displayed (\002OFF\002) or hidden (\002ON\002)."), source.service->nick.c_str()); - return true; - } -}; - class CommandNSSetKill : public Command { public: @@ -817,17 +609,17 @@ class CommandNSSetKill : public Command if (param.equals_ci("ON")) { - nc->ExtendMetadata("KILLPROTECT"); - nc->Shrink("KILL_QUICK"); - nc->Shrink("KILL_IMMED"); + nc->Extend<bool>("KILLPROTECT"); + nc->Shrink<bool>("KILL_QUICK"); + nc->Shrink<bool>("KILL_IMMED"); Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill on for " << nc->display; source.Reply(_("Protection is now \002on\002 for \002%s\002."), nc->display.c_str()); } else if (param.equals_ci("QUICK")) { - nc->ExtendMetadata("KILLPROTECT"); - nc->ExtendMetadata("KILL_QUICK"); - nc->Shrink("KILL_IMMED"); + nc->Extend<bool>("KILLPROTECT"); + nc->Extend<bool>("KILL_QUICK"); + nc->Shrink<bool>("KILL_IMMED"); Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill quick for " << nc->display; source.Reply(_("Protection is now \002on\002 for \002%s\002, with a reduced delay."), nc->display.c_str()); } @@ -835,9 +627,9 @@ class CommandNSSetKill : public Command { if (Config->GetModule(this->owner)->Get<bool>("allowkillimmed")) { - nc->ExtendMetadata("KILLPROTECT"); - nc->ExtendMetadata("KILL_IMMED"); - nc->Shrink("KILL_QUICK"); + nc->Extend<bool>("KILLPROTECT"); + nc->Shrink<bool>("KILL_QUICK"); + nc->Extend<bool>("KILL_IMMED"); Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to set kill immed for " << nc->display; source.Reply(_("Protection is now \002on\002 for \002%s\002, with no delay."), nc->display.c_str()); } @@ -846,9 +638,9 @@ class CommandNSSetKill : public Command } else if (param.equals_ci("OFF")) { - nc->Shrink("KILLPROTECT"); - nc->Shrink("KILL_QUICK"); - nc->Shrink("KILL_IMMED"); + nc->Shrink<bool>("KILLPROTECT"); + nc->Shrink<bool>("KILL_QUICK"); + nc->Shrink<bool>("KILL_IMMED"); Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable kill for " << nc->display; source.Reply(_("Protection is now \002off\002 for \002%s\002."), nc->display.c_str()); } @@ -1053,19 +845,17 @@ class CommandNSSetMessage : public Command if (param.equals_ci("ON")) { Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable " << source.command << " for " << nc->display; - nc->ExtendMetadata("MSG"); + nc->Extend<bool>("MSG"); source.Reply(_("Services will now reply to \002%s\002 with \002messages\002."), nc->display.c_str()); } else if (param.equals_ci("OFF")) { Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable " << source.command << " for " << nc->display; - nc->Shrink("MSG"); + nc->Shrink<bool>("MSG"); source.Reply(_("Services will now reply to \002%s\002 with \002notices\002."), nc->display.c_str()); } else this->OnSyntaxError(source, "MSG"); - - return; } void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override @@ -1115,95 +905,6 @@ class CommandNSSASetMessage : public CommandNSSetMessage } }; -class CommandNSSetPrivate : public Command -{ - public: - CommandNSSetPrivate(Module *creator, const Anope::string &sname = "nickserv/set/private", size_t min = 1) : Command(creator, sname, min, min + 1) - { - this->SetDesc(_("Prevent the nickname from appearing in the LIST command")); - this->SetSyntax(_("{ON | OFF}")); - } - - void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) - { - const NickAlias *na = NickAlias::Find(user); - if (!na) - { - source.Reply(NICK_X_NOT_REGISTERED, user.c_str()); - return; - } - NickCore *nc = na->nc; - - EventReturn MOD_RESULT; - FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param)); - if (MOD_RESULT == EVENT_STOP) - return; - - if (param.equals_ci("ON")) - { - Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable private for " << nc->display; - nc->ExtendMetadata("PRIVATE"); - source.Reply(_("Private option is now \002on\002 for \002%s\002."), nc->display.c_str()); - } - else if (param.equals_ci("OFF")) - { - Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable private for " << nc->display; - nc->Shrink("PRIVATE"); - source.Reply(_("Private option is now \002off\002 for \002%s\002."), nc->display.c_str()); - } - else - this->OnSyntaxError(source, "PRIVATE"); - - return; - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - this->Run(source, source.nc->display, params[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Turns %s's privacy option on or off for your nick.\n" - "With \002PRIVATE\002 set, your nickname will not appear in\n" - "nickname lists generated with %s's \002LIST\002 command.\n" - "(However, anyone who knows your nickname can still get\n" - "information on it using the \002INFO\002 command.)"), - source.service->nick.c_str(), source.service->nick.c_str()); - return true; - } -}; - -class CommandNSSASetPrivate : public CommandNSSetPrivate -{ - public: - CommandNSSASetPrivate(Module *creator) : CommandNSSetPrivate(creator, "nickserv/saset/private", 2) - { - this->ClearSyntax(); - this->SetSyntax(_("\037nickname\037 {ON | OFF}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override - { - this->Run(source, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) anope_override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Turns %s's privacy option on or off for the nick.\n" - "With \002PRIVATE\002 set, the nickname will not appear in\n" - "nickname lists generated with %s's \002LIST\002 command.\n" - "(However, anyone who knows the nickname can still get\n" - "information on it using the \002INFO\002 command.)"), - source.service->nick.c_str(), source.service->nick.c_str()); - return true; - } -}; - class CommandNSSetSecure : public Command { public: @@ -1231,13 +932,13 @@ class CommandNSSetSecure : public Command if (param.equals_ci("ON")) { Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable secure for " << nc->display; - nc->ExtendMetadata("SECURE"); + nc->Extend<bool>("NS_SECURE"); source.Reply(_("Secure option is now \002on\002 for \002%s\002."), nc->display.c_str()); } else if (param.equals_ci("OFF")) { Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable secure for " << nc->display; - nc->Shrink("SECURE"); + nc->Shrink<bool>("NS_SECURE"); source.Reply(_("Secure option is now \002off\002 for \002%s\002."), nc->display.c_str()); } else @@ -1316,13 +1017,13 @@ class CommandNSSASetNoexpire : public Command if (param.equals_ci("ON")) { Log(LOG_ADMIN, source, this) << "to enable noexpire " << na->nc->display; - na->ExtendMetadata("NO_EXPIRE"); + na->Extend<bool>("NS_NO_EXPIRE"); source.Reply(_("Nick %s \002will not\002 expire."), na->nick.c_str()); } else if (param.equals_ci("OFF")) { Log(LOG_ADMIN, source, this) << "to disable noexpire " << na->nc->display; - na->Shrink("NO_EXPIRE"); + na->Shrink<bool>("NS_NO_EXPIRE"); source.Reply(_("Nick %s \002will\002 expire."), na->nick.c_str()); } else @@ -1355,12 +1056,6 @@ class NSSet : public Module CommandNSSetEmail commandnssetemail; CommandNSSASetEmail commandnssasetemail; - - CommandNSSetGreet commandnssetgreet; - CommandNSSASetGreet commandnssasetgreet; - - CommandNSSetHide commandnssethide; - CommandNSSASetHide commandnssasethide; CommandNSSetKill commandnssetkill; CommandNSSASetKill commandnssasetkill; @@ -1374,14 +1069,17 @@ class NSSet : public Module CommandNSSetPassword commandnssetpassword; CommandNSSASetPassword commandnssasetpassword; - CommandNSSetPrivate commandnssetprivate; - CommandNSSASetPrivate commandnssasetprivate; - CommandNSSetSecure commandnssetsecure; CommandNSSASetSecure commandnssasetsecure; CommandNSSASetNoexpire commandnssasetnoexpire; + SerializableExtensibleItem<bool> autoop, chanstats, killprotect, kill_quick, kill_immed, + message, secure, noexpire; + + /* email, passcode */ + PrimitiveExtensibleItem<std::pair<Anope::string, Anope::string > > ns_set_email; + public: NSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), commandnsset(this), commandnssaset(this), @@ -1389,15 +1087,18 @@ class NSSet : public Module commandnssetchanstats(this), commandnssasetchanstats(this), commandnssetdisplay(this), commandnssasetdisplay(this), commandnssetemail(this), commandnssasetemail(this), - commandnssetgreet(this), commandnssasetgreet(this), - commandnssethide(this), commandnssasethide(this), commandnssetkill(this), commandnssasetkill(this), commandnssetlanguage(this), commandnssasetlanguage(this), commandnssetmessage(this), commandnssasetmessage(this), commandnssetpassword(this), commandnssasetpassword(this), - commandnssetprivate(this), commandnssasetprivate(this), commandnssetsecure(this), commandnssasetsecure(this), - commandnssasetnoexpire(this) + commandnssasetnoexpire(this), + + autoop(this, "AUTOOP"), chanstats(this, "NS_STATS"), killprotect(this, "KILLPROTECT"), + kill_quick(this, "KILL_QUICK"), kill_immed(this, "KILL_IMMED"), message(this, "MSG"), + secure(this, "NS_SECURE"), noexpire(this, "NS_NO_EXPIRE"), + + ns_set_email(this, "ns_set_email") { } @@ -1408,16 +1109,15 @@ class NSSet : public Module if (command->name == "nickserv/confirm" && !params.empty() && uac) { - Anope::string *new_email = uac->GetExt<ExtensibleItemClass<Anope::string> *>("ns_set_email"), *passcode = uac->GetExt<ExtensibleItemClass<Anope::string> *>("ns_set_email_passcode"); - if (new_email && passcode) + std::pair<Anope::string, Anope::string> *n = ns_set_email.Get(uac); + if (n) { - if (params[0] == *passcode) + if (params[0] == n->second) { - uac->email = *new_email; + uac->email = n->first; Log(LOG_COMMAND, source, command) << "to confirm their email address change to " << uac->email; source.Reply(_("Your email address has been changed to \002%s\002."), uac->email.c_str()); - uac->Shrink("ns_set_email"); - uac->Shrink("ns_set_email_passcode"); + ns_set_email.Unset(uac); return EVENT_STOP; } } @@ -1431,9 +1131,34 @@ class NSSet : public Module if (chan->ci) { /* Only give modes if autoop is set */ - give_modes &= !user->Account() || user->Account()->HasExt("AUTOOP"); + give_modes &= !user->Account() || autoop.HasExt(user->Account()); } } + + void OnPreNickExpire(NickAlias *na, bool &expire) anope_override + { + if (noexpire.HasExt(na)) + expire = false; + } + + void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override + { + if (!show_hidden) + return; + + if (killprotect.HasExt(na->nc)) + info.AddOption(_("Protection")); + if (secure.HasExt(na->nc)) + info.AddOption(_("Security")); + if (message.HasExt(na->nc)) + info.AddOption(_("Message mode")); + if (autoop.HasExt(na->nc)) + info.AddOption(_("Auto-op")); + if (chanstats.HasExt(na->nc)) + info.AddOption(_("Chanstats")); + if (noexpire.HasExt(na->nc)) + info.AddOption(_("No expire")); + } }; MODULE_INIT(NSSet) diff --git a/modules/commands/ns_set_misc.cpp b/modules/commands/ns_set_misc.cpp index 15fb0ed33..5dfba99cb 100644 --- a/modules/commands/ns_set_misc.cpp +++ b/modules/commands/ns_set_misc.cpp @@ -11,14 +11,24 @@ #include "module.h" +static Module *me; + static std::map<Anope::string, Anope::string> descriptions; -struct NSMiscData : ExtensibleItem, Serializable +struct NSMiscData; +static Anope::map<ExtensibleItem<NSMiscData> *> items; +static ExtensibleItem<NSMiscData> *GetItem(const Anope::string &name); + +struct NSMiscData : Serializable { Serialize::Reference<NickCore> nc; Anope::string name; Anope::string data; + NSMiscData(Extensible *obj) : Serializable("NSMiscData"), nc(anope_dynamic_static_cast<NickCore *>(obj)) + { + } + NSMiscData(NickCore *ncore, const Anope::string &n, const Anope::string &d) : Serializable("NSMiscData"), nc(ncore), name(n), data(d) { } @@ -52,14 +62,27 @@ struct NSMiscData : ExtensibleItem, Serializable } else { - d = new NSMiscData(nc, sname, sdata); - nc->Extend(sname, d); + ExtensibleItem<NSMiscData> *item = GetItem(sname); + if (item) + d = item->Set(nc, NSMiscData(nc, sname, sdata)); } return d; } }; +static ExtensibleItem<NSMiscData> *GetItem(const Anope::string &name) +{ + ExtensibleItem<NSMiscData>* &it = items[name]; + if (!it) + try + { + it = new ExtensibleItem<NSMiscData>(me, name); + } + catch (const ModuleException &) { } + return it; +} + static Anope::string GetAttribute(const Anope::string &command) { size_t sp = command.rfind(' '); @@ -93,16 +116,20 @@ class CommandNSSetMisc : public Command Anope::string scommand = GetAttribute(source.command); Anope::string key = "ns_set_misc:" + scommand; - nc->Shrink(key); + ExtensibleItem<NSMiscData> *item = GetItem(key); + if (item == NULL) + return; + if (!param.empty()) { - nc->Extend(key, new NSMiscData(nc, key, param)); + item->Set(nc, NSMiscData(nc, key, param)); source.Reply(CHAN_SETTING_CHANGED, scommand.c_str(), nc->display.c_str(), param.c_str()); } else + { + item->Unset(nc); source.Reply(CHAN_SETTING_UNSET, scommand.c_str(), nc->display.c_str()); - - return; + } } void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override @@ -155,6 +182,7 @@ class NSSetMisc : public Module NSSetMisc(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), nsmiscdata_type("NSMiscData", NSMiscData::Unserialize), commandnssetmisc(this), commandnssasetmisc(this) { + me = this; } void OnReload(Configuration::Conf *conf) anope_override @@ -182,17 +210,14 @@ class NSSetMisc : public Module void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool ShowHidden) anope_override { - std::deque<Anope::string> list; - na->nc->GetExtList(list); - - for (unsigned i = 0; i < list.size(); ++i) + Anope::map<ExtensibleItem<NSMiscData> *> items; + for (Anope::map<ExtensibleItem<NSMiscData> *>::iterator it = items.begin(); it != items.end(); ++it) { - if (list[i].find("ns_set_misc:") != 0) - continue; + ExtensibleItem<NSMiscData> *e = it->second; + NSMiscData *data = e->Get(na->nc); - NSMiscData *data = na->nc->GetExt<NSMiscData *>(list[i]); - if (data) - info[list[i].substr(12).replace_all_cs("_", " ")] = data->data; + if (data != NULL) + info[e->name.substr(12).replace_all_cs("_", " ")] = data->data; } } }; diff --git a/modules/commands/ns_suspend.cpp b/modules/commands/ns_suspend.cpp index 0db7cd951..76c195a34 100644 --- a/modules/commands/ns_suspend.cpp +++ b/modules/commands/ns_suspend.cpp @@ -10,9 +10,48 @@ */ #include "module.h" +#include "modules/ns_suspend.h" static ServiceReference<NickServService> nickserv("NickServService", "NickServ"); +struct NSSuspendInfoImpl : NSSuspendInfo, Serializable +{ + NSSuspendInfoImpl(Extensible *) : Serializable("NSSuspendInfo") { } + + void Serialize(Serialize::Data &data) const anope_override + { + data["nick"] << nick; + data["by"] << by; + data["reason"] << reason; + data["time"] << when; + data["expires"] << expires; + } + + static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) + { + Anope::string snick; + data["nick"] >> snick; + + NSSuspendInfoImpl *si; + if (obj) + si = anope_dynamic_static_cast<NSSuspendInfoImpl *>(obj); + else + { + NickAlias *na = NickAlias::Find(snick); + if (!na) + return NULL; + si = na->Extend<NSSuspendInfoImpl>("SUSPENDED"); + data["nick"] >> si->nick; + } + + data["bi"] >> si->by; + data["reason"] >> si->reason; + data["time"] >> si->when; + data["expires"] >> si->expires; + return si; + } +}; + class CommandNSSuspend : public Command { public: @@ -60,18 +99,12 @@ class CommandNSSuspend : public Command NickCore *nc = na->nc; - nc->ExtendMetadata("SUSPENDED"); - nc->ExtendMetadata("SECURE"); - nc->Shrink("KILLPROTECT"); - nc->Shrink("KILL_QUICK"); - nc->Shrink("KILL_IMMED"); - - nc->ExtendMetadata("suspend:by", source.GetNick()); - if (!reason.empty()) - nc->ExtendMetadata("suspend:reason", reason); - if (expiry_secs > 0) - nc->ExtendMetadata("suspend:expire", stringify(Anope::CurTime + expiry_secs)); - nc->ExtendMetadata("suspend:time", stringify(Anope::CurTime)); + NSSuspendInfo *si = nc->Extend<NSSuspendInfo>("SUSPENDED"); + si->nick = nc->display; + si->by = source.GetNick(); + si->reason = reason; + si->when = Anope::CurTime; + si->expires = expiry_secs ? expiry_secs + Anope::CurTime : 0; for (unsigned i = 0; i < nc->aliases->size(); ++i) { @@ -81,7 +114,7 @@ class CommandNSSuspend : public Command { na2->last_quit = reason; - User *u2 = User::Find(na2->nick); + User *u2 = User::Find(na2->nick, true); if (u2) { u2->Logout(); @@ -141,18 +174,12 @@ class CommandNSUnSuspend : public Command return; } - na->nc->Shrink("SUSPENDED"); - na->nc->Shrink("suspend:expire"); - na->nc->Shrink("suspend:by"); - na->nc->Shrink("suspend:reason"); - na->nc->Shrink("suspend:time"); + na->nc->Shrink<NSSuspendInfo>("SUSPENDED"); Log(LOG_ADMIN, source, this) << "for " << na->nick; source.Reply(_("Nick %s is now released."), nick.c_str()); FOREACH_MOD(OnNickUnsuspended, (na)); - - return; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -168,55 +195,61 @@ class NSSuspend : public Module { CommandNSSuspend commandnssuspend; CommandNSUnSuspend commandnsunsuspend; + ExtensibleItem<NSSuspendInfoImpl> suspend; + Serialize::Type suspend_type; public: NSSuspend(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandnssuspend(this), commandnsunsuspend(this) + commandnssuspend(this), commandnsunsuspend(this), suspend(this, "SUSPENDED"), + suspend_type("NSSuspendInfo", NSSuspendInfoImpl::Unserialize) { } void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) anope_override { - if (na->nc->HasExt("SUSPENDED")) + NSSuspendInfo *s = suspend.Get(na->nc); + if (s) { - Anope::string *by = na->nc->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:by"), *reason = na->nc->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:reason"), *t = na->nc->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:time"); info["Suspended"] = "This nickname is \2suspended\2."; - if (by) - info["Suspended by"] = *by; - if (reason) - info["Suspend reason"] = *reason; - if (t) - info["Suspended on"] = Anope::strftime(convertTo<time_t>(*t), source.GetAccount(), true); + if (!s->by.empty()) + info["Suspended by"] = s->by; + if (!s->reason.empty()) + info["Suspend reason"] = s->reason; + if (s->when) + info["Suspended on"] = Anope::strftime(s->when, source.GetAccount(), true); + if (s->expires) + info["Suspended expires"] = Anope::strftime(s->expires, source.GetAccount(), true); } } void OnPreNickExpire(NickAlias *na, bool &expire) anope_override { - if (!na->nc->HasExt("SUSPENDED")) + NSSuspendInfo *s = suspend.Get(na->nc); + if (!s) return; expire = false; - Anope::string *str = na->nc->GetExt<ExtensibleItemClass<Anope::string> *>("suspend:expire"); - if (str == NULL) + if (!s->expires) return; - try + if (s->expires < Anope::CurTime) { - time_t when = convertTo<time_t>(*str); - if (when < Anope::CurTime) - { - na->last_seen = Anope::CurTime; - na->nc->Shrink("SUSPENDED"); - na->nc->Shrink("suspend:expire"); - na->nc->Shrink("suspend:by"); - na->nc->Shrink("suspend:reason"); - na->nc->Shrink("suspend:time"); - - Log(LOG_NORMAL, "expire", Config->GetClient("NickServ")) << "Expiring suspend for " << na->nick; - } + na->last_seen = Anope::CurTime; + suspend.Unset(na->nc); + + Log(LOG_NORMAL, "expire", Config->GetClient("NickServ")) << "Expiring suspend for " << na->nick; } - catch (const ConvertException &) { } + } + + EventReturn OnNickValidate(User *u, NickAlias *na) anope_override + { + NSSuspendInfo *s = suspend.Get(na->nc); + if (!s) + return EVENT_CONTINUE; + + u->SendMessage(Config->GetClient("NickServ"), NICK_X_SUSPENDED, u->nick.c_str()); + return EVENT_STOP; } }; diff --git a/modules/commands/os_defcon.cpp b/modules/commands/os_defcon.cpp index 8bea10d0f..58d18558b 100644 --- a/modules/commands/os_defcon.cpp +++ b/modules/commands/os_defcon.cpp @@ -407,13 +407,11 @@ class OSDefcon : public Module this->ParseModeString(); } - EventReturn OnChannelModeSet(Channel *c, MessageSource &, const Anope::string &mname, const Anope::string ¶m) anope_override + EventReturn OnChannelModeSet(Channel *c, MessageSource &, ChannelMode *mode, const Anope::string ¶m) anope_override { - ChannelMode *cm = ModeManager::FindChannelModeByName(mname); - - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && cm && DConfig.DefConModesOff.count(mname)) + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOff.count(mode->name)) { - c->RemoveMode(Config->GetClient("OperServ"), cm, param); + c->RemoveMode(Config->GetClient("OperServ"), mode, param); return EVENT_STOP; } @@ -421,18 +419,16 @@ class OSDefcon : public Module return EVENT_CONTINUE; } - EventReturn OnChannelModeUnset(Channel *c, MessageSource &, const Anope::string &mname, const Anope::string &) anope_override + EventReturn OnChannelModeUnset(Channel *c, MessageSource &, ChannelMode *mode, const Anope::string &) anope_override { - ChannelMode *cm = ModeManager::FindChannelModeByName(mname); - - if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && cm && DConfig.DefConModesOn.count(mname)) + if (DConfig.Check(DEFCON_FORCE_CHAN_MODES) && DConfig.DefConModesOn.count(mode->name)) { Anope::string param; - if (DConfig.GetDefConParam(mname, param)) - c->SetMode(Config->GetClient("OperServ"), cm, param); + if (DConfig.GetDefConParam(mode->name, param)) + c->SetMode(Config->GetClient("OperServ"), mode, param); else - c->SetMode(Config->GetClient("OperServ"), cm); + c->SetMode(Config->GetClient("OperServ"), mode); return EVENT_STOP; diff --git a/modules/commands/os_login.cpp b/modules/commands/os_login.cpp index 9d666188a..6c1edecfb 100644 --- a/modules/commands/os_login.cpp +++ b/modules/commands/os_login.cpp @@ -31,7 +31,7 @@ class CommandOSLogin : public Command source.Reply(_("No oper block for your nick.")); else if (o->password.empty()) source.Reply(_("Your oper block doesn't require logging in.")); - else if (u->HasExt("os_login_password_correct")) + else if (u->HasExt("os_login")) source.Reply(_("You are already identified.")); else if (o->password != password) { @@ -41,11 +41,9 @@ class CommandOSLogin : public Command else { Log(LOG_ADMIN, source, this) << "and successfully identified to " << source.service->nick; - u->Extend("os_login_password_correct"); + u->Extend<bool>("os_login"); source.Reply(_("Password accepted.")); } - - return; } bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override @@ -80,12 +78,12 @@ class CommandOSLogout : public Command source.Reply(_("No oper block for your nick.")); else if (o->password.empty()) source.Reply(_("Your oper block doesn't require logging in.")); - else if (!u->HasExt("os_login_password_correct")) + else if (!u->HasExt("os_login")) source.Reply(_("You are not identified.")); else { Log(LOG_ADMIN, source, this); - u->Shrink("os_login_password_correct"); + u->Shrink<bool>("os_login"); source.Reply(_("You have been logged out.")); } } @@ -110,25 +108,20 @@ class OSLogin : public Module { CommandOSLogin commandoslogin; CommandOSLogout commandoslogout; + ExtensibleItem<bool> os_login; public: OSLogin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandoslogin(this), commandoslogout(this) + commandoslogin(this), commandoslogout(this), os_login(this, "os_login") { } - ~OSLogin() - { - for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) - it->second->Shrink("os_login_password_correct"); - } - EventReturn IsServicesOper(User *u) anope_override { if (!u->Account()->o->password.empty()) { - if (u->HasExt("os_login_password_correct")) + if (os_login.HasExt(u)) return EVENT_ALLOW; return EVENT_STOP; } diff --git a/modules/commands/os_noop.cpp b/modules/commands/os_noop.cpp index 171b11c94..0b5dc74d9 100644 --- a/modules/commands/os_noop.cpp +++ b/modules/commands/os_noop.cpp @@ -35,7 +35,7 @@ class CommandOSNOOP : public Command { /* Remove the O:lines */ IRCD->SendSVSNOOP(s, true); - s->Extend("noop", new ExtensibleItemClass<Anope::string>(source.GetNick())); + s->Extend<Anope::string>("noop", source.GetNick()); Log(LOG_ADMIN, source, this) << "SET on " << s->GetName(); source.Reply(_("All operators from \002%s\002 have been removed."), s->GetName().c_str()); @@ -52,7 +52,7 @@ class CommandOSNOOP : public Command } else if (cmd.equals_ci("REVOKE")) { - s->Shrink("noop"); + s->Shrink<Anope::string>("noop"); IRCD->SendSVSNOOP(s, false); Log(LOG_ADMIN, source, this) << "REVOKE on " << s->GetName(); source.Reply(_("All O:lines of \002%s\002 have been reset."), s->GetName().c_str()); @@ -76,25 +76,23 @@ class CommandOSNOOP : public Command class OSNOOP : public Module { CommandOSNOOP commandosnoop; + PrimitiveExtensibleItem<Anope::string> noop; public: OSNOOP(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), - commandosnoop(this) + commandosnoop(this), noop(this, "noop") { } void OnUserModeSet(User *u, const Anope::string &mname) anope_override { - if (mname == "OPER" && u->server->HasExt("noop")) + Anope::string *setter; + if (mname == "OPER" && (setter = noop.Get(u->server))) { - Anope::string *setter = u->server->GetExt<ExtensibleItemClass<Anope::string> *>("noop"); - if (setter) - { - Anope::string reason = "NOOP command used by " + *setter; - BotInfo *OperServ = Config->GetClient("OperServ"); - u->Kill(OperServ ? OperServ->nick : "", reason); - } + Anope::string reason = "NOOP command used by " + *setter; + BotInfo *OperServ = Config->GetClient("OperServ"); + u->Kill(OperServ ? OperServ->nick : "", reason); } } }; |