diff options
author | Sadie Powell <sadie@witchery.services> | 2025-03-08 13:36:41 +0000 |
---|---|---|
committer | Sadie Powell <sadie@witchery.services> | 2025-03-08 14:07:15 +0000 |
commit | 62bfa33464df8cc413fa4f111ab62e189be0ca32 (patch) | |
tree | 5dd8b0bb0caa1fc3c76e05253641f332a3c10af4 | |
parent | b4ab7dadb94c463b46e8bef5bce8c8c531bf1995 (diff) |
Rework how nickname protection works.
- Rename the command and module from kill to protect (this command
hasn't actually killed users in a long time).
- Replace QUICK/IMMED with a duration option.
-rw-r--r-- | data/anope.example.conf | 4 | ||||
-rw-r--r-- | data/nickserv.example.conf | 62 | ||||
-rw-r--r-- | language/anope.en_US.po | 70 | ||||
-rw-r--r-- | modules/database/db_atheme.cpp | 29 | ||||
-rw-r--r-- | modules/database/db_old.cpp | 15 | ||||
-rw-r--r-- | modules/nickserv/nickserv.cpp | 32 | ||||
-rw-r--r-- | modules/nickserv/ns_set_kill.cpp | 189 | ||||
-rw-r--r-- | modules/nickserv/ns_set_protect.cpp | 168 | ||||
-rw-r--r-- | modules/webcpanel/pages/nickserv/info.cpp | 56 | ||||
-rw-r--r-- | modules/webcpanel/templates/nickserv/info.html | 14 | ||||
-rw-r--r-- | src/nickcore.cpp | 24 |
11 files changed, 332 insertions, 331 deletions
diff --git a/data/anope.example.conf b/data/anope.example.conf index 4c73deaa3..ad85cd364 100644 --- a/data/anope.example.conf +++ b/data/anope.example.conf @@ -746,8 +746,8 @@ log * nickserv/getemail nickserv/suspend nickserv/ajoin nickserv/list * * nickserv/saset/autoop nickserv/saset/display nickserv/saset/email nickserv/saset/greet - * nickserv/saset/kill nickserv/saset/keepmodes nickserv/saset/language nickserv/saset/message - * nickserv/saset/neverop nickserv/saset/noexpire nickserv/saset/password nickserv/saset/private + * nickserv/saset/keepmodes nickserv/saset/language nickserv/saset/message nickserv/saset/neverop + * nickserv/saset/noexpire nickserv/saset/password nickserv/saset/private nickserv/saset/protect * nickserv/saset/url * * hostserv/set hostserv/del hostserv/list diff --git a/data/nickserv.example.conf b/data/nickserv.example.conf index 6c0ca4cda..d4e5c28fe 100644 --- a/data/nickserv.example.conf +++ b/data/nickserv.example.conf @@ -126,7 +126,7 @@ module * This directive is optional, if left blank, the options will default to memo_signon, and * memo_receive. If you really want no defaults, use "none" by itself as the option. */ - defaults = "killprotect ns_private hide_email hide_mask memo_signon memo_receive autoop" + defaults = "autoop hide_email hide_mask memo_receive memo_signon ns_private protect" /* * The minimum length of time between consecutive uses of NickServ's REGISTER command. This @@ -171,11 +171,25 @@ module hidenetsplitquit = no /* - * If set, is the length of time NickServ's killquick and kill options wait before - * forcing users off of protected nicknames. + * The default period to force users to stop using a protected nickname after. + * + * Defaults to 1 minute. + */ + defaultprotect = 1m + + /* + * The minimum period that a user can have a user forced off their protected nickname after. + * + * Defaults to 10 seconds. + */ + minprotect = 10s + + /* + * The maximum period that a user can have a user forced off their protected nickname after. + * + * Defaults to 10 minutes. */ - killquick = 20s - kill = 60s + maxprotect = 10m /* * If set, forbids the registration of nicks that contain an existing @@ -597,28 +611,6 @@ command { service = "NickServ"; name = "SET KEEPMODES"; command = "nickserv/set/ command { service = "NickServ"; name = "SASET KEEPMODES"; command = "nickserv/saset/keepmodes"; permission = "nickserv/saset/keepmodes"; } /* - * ns_set_kill - * - * Provides the commands nickserv/set/kill and kickserv/saset/kill. - * - * Used for configuring nickname protection. - */ -module -{ - name = "ns_set_kill" - - /* - * Allow the use of the IMMED option in the NickServ SET KILL command. - * - * This directive is optional. - */ - #allowkillimmed = yes -} - -command { service = "NickServ"; name = "SET KILL"; command = "nickserv/set/kill"; } -command { service = "NickServ"; name = "SASET KILL"; command = "nickserv/saset/kill"; permission = "nickserv/saset/kill"; } - -/* * ns_set_language * * Provides the command nickserv/set/language and nickserv/saset/language. @@ -663,6 +655,22 @@ command { service = "NickServ"; name = "SASET URL"; command = "nickserv/saset/mi #command { service = "NickServ"; name = "SASET TIMEZONE"; command = "nickserv/saset/misc"; misc_description = _("Associate a time zone with this account"); permission = "nickserv/saset/timezone"; group = "nickserv/admin"; } /* + * ns_set_protect + * + * Provides the commands nickserv/set/protect and kickserv/saset/protect. + * + * Used for configuring nickname protection. + */ +module { name = "ns_set_protect" } + +command { service = "NickServ"; name = "SET PROTECT"; command = "nickserv/set/protect"; } +command { service = "NickServ"; name = "SASET PROTECT"; command = "nickserv/saset/protect"; permission = "nickserv/saset/kill"; } + +# For compatibility with Anope 2.0. +command { service = "NickServ"; name = "SET KILL"; command = "nickserv/set/protect"; hide = true; } +command { service = "NickServ"; name = "SASET KILL"; command = "nickserv/saset/protect"; permission = "nickserv/saset/protect"; hide = true; } + +/* * ns_suspend * * Provides the commands nickserv/suspend and nickserv/unsuspend. diff --git a/language/anope.en_US.po b/language/anope.en_US.po index 6141bd51c..647f09e71 100644 --- a/language/anope.en_US.po +++ b/language/anope.en_US.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: Anope\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-14 14:57+0000\n" -"PO-Revision-Date: 2024-11-25 01:32+0000\n" +"POT-Creation-Date: 2025-03-08 13:33+0000\n" +"PO-Revision-Date: 2025-03-08 13:33+0000\n" "Last-Translator: Sadie Powell <sadie@witchery.services>\n" "Language-Team: English\n" "Language: en_US\n" @@ -659,10 +659,10 @@ msgstr "" msgid "nickname {EMAIL | STATUS | USERMASK | QUIT} {ON | OFF}" msgstr "" -msgid "nickname {ON | OFF}" +msgid "nickname {ON | delay | OFF}" msgstr "" -msgid "nickname {ON | QUICK | IMMED | OFF}" +msgid "nickname {ON | OFF}" msgstr "" msgid "option (channel | bot) settings" @@ -3574,9 +3574,6 @@ msgstr "" msgid "Ignore list:" msgstr "" -msgid "Immediate protection" -msgstr "" - msgid "Incorrect email address." msgstr "" @@ -4740,15 +4737,15 @@ msgid "Protection" msgstr "" #, c-format -msgid "Protection is now off for %s." +msgid "Protection delay must be between %s and %s." msgstr "" #, c-format -msgid "Protection is now on for %s, with a reduced delay." +msgid "Protection is now off for %s." msgstr "" #, c-format -msgid "Protection is now on for %s, with no delay." +msgid "Protection is now on after %lu seconds for %s." msgstr "" #, c-format @@ -4761,9 +4758,6 @@ msgid "" "then enforces the AKILL." msgstr "" -msgid "Quick protection" -msgstr "" - msgid "RANDOMNEWS {ADD|DEL|LIST} [text|num]" msgstr "" @@ -5984,9 +5978,6 @@ msgid "" "shown to users when they join the channel." msgstr "" -msgid "The IMMED option is not available on this network." -msgstr "" - #, c-format msgid "" "The LEVELS command allows fine control over the meaning of\n" @@ -6515,42 +6506,26 @@ msgid "" "information on it using the INFO command.)" msgstr "" -msgid "Turns chanstats channel statistics ON or OFF for this user." -msgstr "" - -msgid "Turns chanstats statistics ON or OFF." -msgstr "" - #, c-format msgid "" -"Turns the automatic protection option for the nick\n" -"on or off. With protection on, if another user\n" -"tries to take the nick, they will be given one minute to\n" -"change to another nick, after which %s will forcibly change\n" -"their nick.\n" -" \n" -"If you select QUICK, the user will be given only 20 seconds\n" -"to change nicks instead of the usual 60. If you select\n" -"IMMED, the user's nick will be changed immediately without being\n" -"warned first or given a chance to change their nick; please\n" -"do not use this option unless necessary. Also, your\n" -"network's administrators may have disabled this option." +"Turns automatic protection for the nick on or off. With\n" +"protection on if a user tries to use a nickname from the\n" +"nick's group they will be given some time to change their\n" +"nick after which %s will forcibly change their nick." msgstr "" #, c-format msgid "" -"Turns the automatic protection option for your nick\n" -"on or off. With protection on, if another user\n" -"tries to take your nick, they will be given one minute to\n" -"change to another nick, after which %s will forcibly change\n" -"their nick.\n" -" \n" -"If you select QUICK, the user will be given only 20 seconds\n" -"to change nicks instead of the usual 60. If you select\n" -"IMMED, the user's nick will be changed immediately without being\n" -"warned first or given a chance to change their nick; please\n" -"do not use this option unless necessary. Also, your\n" -"network's administrators may have disabled this option." +"Turns automatic protection for your account on or off. With\n" +"protection on if another user tries to use a nickname from\n" +"your group they will be given some time to change their nick\n" +"after which %s will forcibly change their nick." +msgstr "" + +msgid "Turns chanstats channel statistics ON or OFF for this user." +msgstr "" + +msgid "Turns chanstats statistics ON or OFF." msgstr "" msgid "Type" @@ -7419,3 +7394,6 @@ msgstr "" msgid "{nick | channel} memo-text" msgstr "" + +msgid "{ON | delay | OFF}" +msgstr "" diff --git a/modules/database/db_atheme.cpp b/modules/database/db_atheme.cpp index 6cac99449..860e81853 100644 --- a/modules/database/db_atheme.cpp +++ b/modules/database/db_atheme.cpp @@ -132,7 +132,6 @@ struct ChannelData final struct UserData final { - bool kill = false; Anope::string info_adder; Anope::string info_message; time_t info_ts = 0; @@ -140,6 +139,8 @@ struct UserData final Anope::string last_quit; Anope::string last_real_mask; bool noexpire = false; + bool protect = false; + std::optional<time_t> protectafter; Anope::string suspend_by; Anope::string suspend_reason; time_t suspend_ts = 0; @@ -1143,22 +1144,9 @@ private: if (key == "private:autojoin") return true; // TODO else if (key == "private:doenforce") - data->kill = true; + data->protect = true; else if (key == "private:enforcetime") - { - if (!data->kill) - return true; // Don't apply this. - - auto kill = Config->GetModule("nickserv").Get<time_t>("kill", "60s"); - auto killquick = Config->GetModule("nickserv").Get<time_t>("killquick", "20s"); - auto secs = Anope::Convert<time_t>(value, kill); - if (secs >= kill) - nc->Extend<bool>("KILLPROTECT"); - else if (secs >= killquick) - nc->Shrink<bool>("KILL_QUICK"); - else - nc->Shrink<bool>("KILL_IMMED"); - } + data->protectafter = Anope::TryConvert<time_t>(value); else if (key == "private:freeze:freezer") data->suspend_by = value; else if (key == "private:freeze:reason") @@ -1371,7 +1359,7 @@ private: ApplyPassword(nc, flags, pass); // No equivalent: bglmNQrS - ApplyFlags(nc, flags, 'E', "KILLPROTECT"); + ApplyFlags(nc, flags, 'E', "PROTECT"); ApplyFlags(nc, flags, 'e', "MEMO_MAIL"); ApplyFlags(nc, flags, 'n', "NEVEROP"); ApplyFlags(nc, flags, 'o', "AUTOOP", false); @@ -1649,6 +1637,13 @@ public: } } + if (data->protect) + { + nc->Extend<bool>("PROTECT"); + if (data->protectafter) + nc->Extend("PROTECT_AFTER", data->protectafter.value()); + } + if (!data->suspend_reason.empty()) { SuspendInfo si; diff --git a/modules/database/db_old.cpp b/modules/database/db_old.cpp index 7f601068f..9812d291b 100644 --- a/modules/database/db_old.cpp +++ b/modules/database/db_old.cpp @@ -480,7 +480,10 @@ static void LoadNicks() READ(read_uint32(&u32, f)); if (u32 & OLD_NI_KILLPROTECT) - nc->Extend<bool>("KILLPROTECT"); + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", 60); + } if (u32 & OLD_NI_MSG) nc->Extend<bool>("MSG"); if (u32 & OLD_NI_MEMO_HARDMAX) @@ -498,9 +501,15 @@ static void LoadNicks() if (u32 & OLD_NI_HIDE_QUIT) nc->Extend<bool>("HIDE_QUIT"); if (u32 & OLD_NI_KILL_QUICK) - nc->Extend<bool>("KILL_QUICK"); + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", 20); + } if (u32 & OLD_NI_KILL_IMMED) - nc->Extend<bool>("KILL_IMMED"); + { + nc->Extend<bool>("PROTECT"); + nc->Extend<time_t>("PROTECT_AFTER", 0); + } if (u32 & OLD_NI_MEMO_MAIL) nc->Extend<bool>("MEMO_MAIL"); if (u32 & OLD_NI_HIDE_STATUS) diff --git a/modules/nickserv/nickserv.cpp b/modules/nickserv/nickserv.cpp index 7d34061c7..c09367914 100644 --- a/modules/nickserv/nickserv.cpp +++ b/modules/nickserv/nickserv.cpp @@ -211,31 +211,27 @@ public: if (Config->GetModule("nickserv").Get<bool>("nonicknameownership")) return; - if (!na->nc->HasExt("KILL_IMMED")) + if (na->nc->HasExt("PROTECT")) { - u->SendMessage(NickServ, NICK_IS_SECURE, NickServ->GetQueryCommand().c_str()); - } - if (na->nc->HasExt("KILLPROTECT")) - { - if (na->nc->HasExt("KILL_IMMED")) - { - u->SendMessage(NickServ, FORCENICKCHANGE_NOW); - this->Collide(u, na); - } - else if (na->nc->HasExt("KILL_QUICK")) + auto &block = Config->GetModule(this); + auto protectafter = na->nc->GetExt<time_t>("PROTECT_AFTER"); + + auto protect = protectafter ? *protectafter : block.Get<time_t>("defaultprotect", "1m"); + protect = std::clamp(protect, block.Get<time_t>("minprotect", "10s"), block.Get<time_t>("maxprotect", "10m")); + + if (protect) { - time_t killquick = Config->GetModule("nickserv").Get<time_t>("killquick", "20s"); - u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(killquick, u->Account()).c_str()); - new NickServCollide(this, this, u, na, killquick); + u->SendMessage(NickServ, NICK_IS_SECURE, NickServ->GetQueryCommand().c_str()); + u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."), + Anope::Duration(protect, u->Account()).c_str()); + new NickServCollide(this, this, u, na, protect); } else { - time_t kill = Config->GetModule("nickserv").Get<time_t>("kill", "60s"); - u->SendMessage(NickServ, _("If you do not change within %s, I will change your nick."), Anope::Duration(kill, u->Account()).c_str()); - new NickServCollide(this, this, u, na, kill); + u->SendMessage(NickServ, FORCENICKCHANGE_NOW); + this->Collide(u, na); } } - } void OnUserLogin(User *u) override diff --git a/modules/nickserv/ns_set_kill.cpp b/modules/nickserv/ns_set_kill.cpp deleted file mode 100644 index 0338c4bb8..000000000 --- a/modules/nickserv/ns_set_kill.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* NickServ core functions - * - * (C) 2003-2025 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - */ - -#include "module.h" - -class CommandNSSetKill - : public Command -{ -public: - CommandNSSetKill(Module *creator, const Anope::string &sname = "nickserv/set/kill", size_t min = 1) - : Command(creator, sname, min, min + 1) - { - this->SetDesc(_("Turn protection on or off")); - this->SetSyntax("{ON | QUICK | IMMED | OFF}"); - } - - void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) - { - if (Anope::ReadOnly) - { - source.Reply(READ_ONLY_MODE); - return; - } - - if (Config->GetModule("nickserv").Get<bool>("nonicknameownership")) - { - source.Reply(_("This command may not be used on this network because nickname ownership is disabled.")); - return; - } - - 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")) - { - 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->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()); - } - else if (param.equals_ci("IMMED")) - { - if (Config->GetModule(this->owner).Get<bool>("allowkillimmed")) - { - 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()); - } - else - source.Reply(_("The \002IMMED\002 option is not available on this network.")); - } - else if (param.equals_ci("OFF")) - { - 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()); - } - else - this->OnSyntaxError(source, "KILL"); - - return; - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override - { - this->Run(source, source.nc->display, params[0]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Turns the automatic protection option for your nick\n" - "on or off. With protection on, if another user\n" - "tries to take your nick, they will be given one minute to\n" - "change to another nick, after which %s will forcibly change\n" - "their nick.\n" - " \n" - "If you select \002QUICK\002, the user will be given only 20 seconds\n" - "to change nicks instead of the usual 60. If you select\n" - "\002IMMED\002, the user's nick will be changed immediately \037without\037 being\n" - "warned first or given a chance to change their nick; please\n" - "do not use this option unless necessary. Also, your\n" - "network's administrators may have disabled this option."), source.service->nick.c_str()); - return true; - } -}; - -class CommandNSSASetKill final - : public CommandNSSetKill -{ -public: - CommandNSSASetKill(Module *creator) - : CommandNSSetKill(creator, "nickserv/saset/kill", 2) - { - this->ClearSyntax(); - this->SetSyntax(_("\037nickname\037 {ON | QUICK | IMMED | OFF}")); - } - - void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override - { - this->Run(source, params[0], params[1]); - } - - bool OnHelp(CommandSource &source, const Anope::string &) override - { - this->SendSyntax(source); - source.Reply(" "); - source.Reply(_("Turns the automatic protection option for the nick\n" - "on or off. With protection on, if another user\n" - "tries to take the nick, they will be given one minute to\n" - "change to another nick, after which %s will forcibly change\n" - "their nick.\n" - " \n" - "If you select \002QUICK\002, the user will be given only 20 seconds\n" - "to change nicks instead of the usual 60. If you select\n" - "\002IMMED\002, the user's nick will be changed immediately \037without\037 being\n" - "warned first or given a chance to change their nick; please\n" - "do not use this option unless necessary. Also, your\n" - "network's administrators may have disabled this option."), source.service->nick.c_str()); - return true; - } -}; - -class NSSetKill final - : public Module -{ -private: - CommandNSSetKill commandnssetkill; - CommandNSSASetKill commandnssasetkill; - SerializableExtensibleItem<bool> killprotect, kill_quick, kill_immed; - -public: - NSSetKill(const Anope::string &modname, const Anope::string &creator) - : Module(modname, creator, VENDOR) - , commandnssetkill(this) - , commandnssasetkill(this) - , killprotect(this, "KILLPROTECT") - , kill_quick(this, "KILL_QUICK") - , kill_immed(this, "KILL_IMMED") - { - } - - void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) override - { - if (!show_hidden) - return; - - if (kill_immed.HasExt(na->nc)) - info.AddOption(_("Immediate protection")); - else if (kill_quick.HasExt(na->nc)) - info.AddOption(_("Quick protection")); - else if (killprotect.HasExt(na->nc)) - info.AddOption(_("Protection")); - } -}; - -MODULE_INIT(NSSetKill) diff --git a/modules/nickserv/ns_set_protect.cpp b/modules/nickserv/ns_set_protect.cpp new file mode 100644 index 000000000..12c995486 --- /dev/null +++ b/modules/nickserv/ns_set_protect.cpp @@ -0,0 +1,168 @@ +/* NickServ core functions + * + * (C) 2003-2025 Anope Team + * Contact us at team@anope.org + * + * Please read COPYING and README for 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 CommandNSSetProtect + : public Command +{ +public: + CommandNSSetProtect(Module *creator, const Anope::string &sname = "nickserv/set/protect", size_t min = 1) + : Command(creator, sname, min, min + 1) + { + this->SetDesc(_("Turn protection on or off")); + this->SetSyntax(_("{ON | \037delay\037 | OFF}")); + } + + void Run(CommandSource &source, const Anope::string &user, const Anope::string ¶m) + { + if (Anope::ReadOnly) + { + source.Reply(READ_ONLY_MODE); + return; + } + + if (Config->GetModule("nickserv").Get<bool>("nonicknameownership")) + { + source.Reply(_("This command may not be used on this network because nickname ownership is disabled.")); + return; + } + + const auto *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")) + { + nc->Extend<bool>("PROTECT"); + nc->Shrink<time_t>("PROTECT_AFTER"); + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable protection for " << nc->display; + source.Reply(_("Protection is now \002on\002 for \002%s\002."), nc->display.c_str()); + } + else if (param.equals_ci("OFF")) + { + nc->Shrink<bool>("PROTECT"); + nc->Shrink<time_t>("PROTECT_AFTER"); + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable protection for " << nc->display; + source.Reply(_("Protection is now \002off\002 for \002%s\002."), nc->display.c_str()); + } + else + { + auto iparam = Anope::TryConvert<time_t>(param); + if (!iparam) + { + this->OnSyntaxError(source, "PROTECT"); + return; + } + + auto &block = Config->GetModule("nickserv"); + auto minprotect = block.Get<time_t>("minprotect", "10s"); + auto maxprotect = block.Get<time_t>("maxprotect", "10m"); + if (*iparam < minprotect || *iparam > maxprotect) + { + source.Reply(_("Protection delay must be between %s and %s."), + Anope::Duration(minprotect, source.GetAccount()).c_str(), + Anope::Duration(maxprotect, source.GetAccount()).c_str()); + return; + } + + nc->Extend<bool>("PROTECT"); + nc->Extend<time_t>("PROTECT_AFTER", *iparam); + Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable protection after " << *iparam << " seconds for " << nc->display; + source.Reply(_("Protection is now \002on\002 after \002%lu seconds\002 for \002%s\002."), *iparam, nc->display.c_str()); + } + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + this->Run(source, source.nc->display, params[0]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Turns automatic protection for your account on or off. With\n" + "protection on if another user tries to use a nickname from\n" + "your group they will be given some time to change their nick\n" + "after which %s will forcibly change their nick."), source.service->nick.c_str()); + return true; + } +}; + +class CommandNSSASetProtect final + : public CommandNSSetProtect +{ +public: + CommandNSSASetProtect(Module *creator) + : CommandNSSetProtect(creator, "nickserv/saset/protect", 2) + { + this->ClearSyntax(); + this->SetSyntax(_("\037nickname\037 {ON | \037delay\037 | OFF}")); + } + + void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override + { + this->Run(source, params[0], params[1]); + } + + bool OnHelp(CommandSource &source, const Anope::string &) override + { + this->SendSyntax(source); + source.Reply(" "); + source.Reply(_("Turns automatic protection for the nick on or off. With\n" + "protection on if a user tries to use a nickname from the\n" + "nick's group they will be given some time to change their\n" + "nick after which %s will forcibly change their nick."), source.service->nick.c_str()); + return true; + } +}; + +class NSSetProtect final + : public Module +{ +private: + CommandNSSetProtect commandnssetprotect; + CommandNSSASetProtect commandnssasetprotect; + + SerializableExtensibleItem<bool> protect; + SerializableExtensibleItem<time_t> protectafter; + +public: + NSSetProtect(const Anope::string &modname, const Anope::string &creator) + : Module(modname, creator, VENDOR) + , commandnssetprotect(this) + , commandnssasetprotect(this) + , protect(this, "PROTECT") + , protectafter(this, "PROTECT_AFTER") + { + } + + void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_hidden) override + { + if (!show_hidden) + return; + + if (protect.HasExt(na->nc)) + info.AddOption(_("Protection")); + } +}; + +MODULE_INIT(NSSetProtect) diff --git a/modules/webcpanel/pages/nickserv/info.cpp b/modules/webcpanel/pages/nickserv/info.cpp index e1a44a426..be8b110f5 100644 --- a/modules/webcpanel/pages/nickserv/info.cpp +++ b/modules/webcpanel/pages/nickserv/info.cpp @@ -56,23 +56,38 @@ bool WebCPanel::NickServ::Info::OnRequest(HTTPProvider *server, const Anope::str na->nc->Shrink<bool>("NS_PRIVATE"); replacements["MESSAGES"] = "Private updated"; } - if (message.post_data["kill"] == "on" && !na->nc->HasExt("KILLPROTECT")) + if (na->nc->HasExt("PROTECT") != !!message.post_data.count("protect")) { - na->nc->Extend<bool>("KILLPROTECT"); - na->nc->Shrink<bool>("KILL_QUICK"); - replacements["MESSAGES"] = "Kill updated"; - } - else if (message.post_data["kill"] == "quick" && !na->nc->HasExt("KILL_QUICK")) - { - na->nc->Extend<bool>("KILLPROTECT"); - na->nc->Extend<bool>("KILL_QUICK"); - replacements["MESSAGES"] = "Kill updated"; + if (!na->nc->HasExt("PROTECT")) + { + na->nc->Shrink<bool>("PROTECT"); + na->nc->Shrink<time_t>("PROTECT_AFTER"); + } + else + { + na->nc->Extend<bool>("PROTECT"); + } + replacements["MESSAGES"] = "Protect updated"; } - else if (message.post_data["kill"] == "off" && (!!na->nc->HasExt("KILLPROTECT") || !!na->nc->HasExt("KILL_QUICK"))) + if (na->nc->HasExt("PROTECT") && message.post_data.count("protect_after") > 0) { - na->nc->Shrink<bool>("KILLPROTECT"); - na->nc->Shrink<bool>("KILL_QUICK"); - replacements["MESSAGES"] = "Kill updated"; + auto &block = Config->GetModule("nickserv"); + auto minprotect = block.Get<time_t>("minprotect", "10s"); + auto maxprotect = block.Get<time_t>("maxprotect", "10m"); + + auto secs = Anope::TryConvert<time_t>(message.post_data["greet"]); + if (!secs) + replacements["ERRORS"] = "Protection after seconds are not valid"; + else if (*secs < minprotect || *secs > maxprotect) + { + replacements["ERRORS"] = Anope::printf("Protection delay must be between %ld and %ld seconds.", + minprotect, maxprotect); + } + else + { + na->nc->Extend<time_t>("PROTECT_AFTER", *secs); + replacements["MESSAGES"] = "Protect after updated"; + } } if (na->nc->HasExt("NS_KEEP_MODES") != !!message.post_data.count("keepmodes")) { @@ -113,12 +128,13 @@ bool WebCPanel::NickServ::Info::OnRequest(HTTPProvider *server, const Anope::str replacements["AUTOOP"]; if (na->nc->HasExt("NS_PRIVATE")) replacements["PRIVATE"]; - if (na->nc->HasExt("KILLPROTECT")) - replacements["KILL_ON"]; - if (na->nc->HasExt("KILL_QUICK")) - replacements["KILL_QUICK"]; - if (!na->nc->HasExt("KILLPROTECT") && !na->nc->HasExt("KILL_QUICK")) - replacements["KILL_OFF"]; + if (na->nc->HasExt("PROTECT")) + { + replacements["PROTECT"]; + auto *protectafter = na->nc->GetExt<time_t>("PROTECT_AFTER"); + if (protectafter) + replacements["PROTECT_AFTER"] = *protectafter; + } if (na->nc->HasExt("NS_KEEP_MODES")) replacements["KEEPMODES"]; if (na->nc->HasExt("MSG")) diff --git a/modules/webcpanel/templates/nickserv/info.html b/modules/webcpanel/templates/nickserv/info.html index c713cd64c..794598196 100644 --- a/modules/webcpanel/templates/nickserv/info.html +++ b/modules/webcpanel/templates/nickserv/info.html @@ -61,14 +61,12 @@ <td><input type="checkbox" name="private" value="on" {IF EXISTS PRIVATE}checked{END IF}></td> </tr> <tr> - <td>Kill:</td> - <td> - <select name="kill" class="form-control input-sm"> - <option value="on" {IF EXISTS KILL_ON}selected{END IF}>On</option> - <option value="quick" {IF EXISTS KILL_QUICK}selected{END IF}>Quick</option> - <option value="off" {IF EXISTS KILL_OFF}selected{END IF}>Off</option> - </select> - </td> + <td>Protect:</td> + <td><input type="checkbox" name="protect" value="on" {IF EXISTS PROTECT}checked{END IF}></td> + </tr> + <tr> + <td>Protect after:</td> + <td><input name="protect_after" value="{PROTECT_AFTER}" class="form-control input-sm"></td> </tr> </tbody> </table> diff --git a/src/nickcore.cpp b/src/nickcore.cpp index 756b40d83..9b44dc61a 100644 --- a/src/nickcore.cpp +++ b/src/nickcore.cpp @@ -146,7 +146,29 @@ Serializable *NickCore::Unserialize(Serializable *obj, Serialize::Data &data) b = false; data["extensible:KILLPROTECT"] >> b; if (b) - nc->Extend<bool>("KILLPROTECT"); + nc->Extend<bool>("PROTECT"); + + b = false; + data["KILLPROTECT"] >> b; + if (b) + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", Config->GetModule("nickserv").Get<time_t>("kill", "60s")); + } + b = false; + data["KILL_QUICK"] >> b; + if (b) + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", Config->GetModule("nickserv").Get<time_t>("killquick", "20s")); + } + b = false; + data["KILL_IMMED"] >> b; + if (b) + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", 0); + } /* end compat */ return nc; |