anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

ns_list.cpp (9718B)

      1 /* NickServ core functions
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "module.h"
     13 
     14 class CommandNSList : public Command
     15 {
     16  public:
     17 	CommandNSList(Module *creator) : Command(creator, "nickserv/list", 1, 2)
     18 	{
     19 		this->SetDesc(_("List all registered nicknames that match a given pattern"));
     20 		this->SetSyntax(_("\037pattern\037 [SUSPENDED] [NOEXPIRE] [UNCONFIRMED]"));
     21 	}
     22 
     23 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     24 	{
     25 
     26 		Anope::string pattern = params[0];
     27 		const NickCore *mync;
     28 		unsigned nnicks;
     29 		bool is_servadmin = source.HasCommand("nickserv/list");
     30 		int count = 0, from = 0, to = 0;
     31 		bool suspended, nsnoexpire, unconfirmed;
     32 		unsigned listmax = Config->GetModule(this->owner)->Get<unsigned>("listmax", "50");
     33 
     34 		suspended = nsnoexpire = unconfirmed = false;
     35 
     36 		if (pattern[0] == '#')
     37 		{
     38 			Anope::string n1, n2;
     39 			sepstream(pattern.substr(1), '-').GetToken(n1, 0);
     40 			sepstream(pattern, '-').GetToken(n2, 1);
     41 			try
     42 			{
     43 				from = convertTo<int>(n1);
     44 				to = convertTo<int>(n2);
     45 			}
     46 			catch (const ConvertException &)
     47 			{
     48 				source.Reply(LIST_INCORRECT_RANGE);
     49 				return;
     50 			}
     51 
     52 			pattern = "*";
     53 		}
     54 
     55 		nnicks = 0;
     56 
     57 		if (is_servadmin && params.size() > 1)
     58 		{
     59 			Anope::string keyword;
     60 			spacesepstream keywords(params[1]);
     61 			while (keywords.GetToken(keyword))
     62 			{
     63 				if (keyword.equals_ci("NOEXPIRE"))
     64 					nsnoexpire = true;
     65 				if (keyword.equals_ci("SUSPENDED"))
     66 					suspended = true;
     67 				if (keyword.equals_ci("UNCONFIRMED"))
     68 					unconfirmed = true;
     69 			}
     70 		}
     71 
     72 		mync = source.nc;
     73 		ListFormatter list(source.GetAccount());
     74 
     75 		list.AddColumn(_("Nick")).AddColumn(_("Last usermask"));
     76 
     77 		Anope::map<NickAlias *> ordered_map;
     78 		for (nickalias_map::const_iterator it = NickAliasList->begin(), it_end = NickAliasList->end(); it != it_end; ++it)
     79 			ordered_map[it->first] = it->second;
     80 
     81 		for (Anope::map<NickAlias *>::const_iterator it = ordered_map.begin(), it_end = ordered_map.end(); it != it_end; ++it)
     82 		{
     83 			const NickAlias *na = it->second;
     84 
     85 			/* Don't show private nicks to non-services admins. */
     86 			if (na->nc->HasExt("NS_PRIVATE") && !is_servadmin && na->nc != mync)
     87 				continue;
     88 			else if (nsnoexpire && !na->HasExt("NS_NO_EXPIRE"))
     89 				continue;
     90 			else if (suspended && !na->nc->HasExt("NS_SUSPENDED"))
     91 				continue;
     92 			else if (unconfirmed && !na->nc->HasExt("UNCONFIRMED"))
     93 				continue;
     94 
     95 			/* We no longer compare the pattern against the output buffer.
     96 			 * Instead we build a nice nick!user@host buffer to compare.
     97 			 * The output is then generated separately. -TheShadow */
     98 			Anope::string buf = Anope::printf("%s!%s", na->nick.c_str(), !na->last_usermask.empty() ? na->last_usermask.c_str() : "*@*");
     99 			if (na->nick.equals_ci(pattern) || Anope::Match(buf, pattern, false, true))
    100 			{
    101 				if (((count + 1 >= from && count + 1 <= to) || (!from && !to)) && ++nnicks <= listmax)
    102 				{
    103 					bool isnoexpire = false;
    104 					if (is_servadmin && na->HasExt("NS_NO_EXPIRE"))
    105 						isnoexpire = true;
    106 
    107 					ListFormatter::ListEntry entry;
    108 					entry["Nick"] = (isnoexpire ? "!" : "") + na->nick;
    109 					if (na->nc->HasExt("HIDE_MASK") && !is_servadmin && na->nc != mync)
    110 						entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Hostname hidden]"));
    111 					else if (na->nc->HasExt("NS_SUSPENDED"))
    112 						entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Suspended]"));
    113 					else if (na->nc->HasExt("UNCONFIRMED"))
    114 						entry["Last usermask"] = Language::Translate(source.GetAccount(), _("[Unconfirmed]"));
    115 					else
    116 						entry["Last usermask"] = na->last_usermask;
    117 					list.AddEntry(entry);
    118 				}
    119 				++count;
    120 			}
    121 		}
    122 
    123 		source.Reply(_("List of entries matching \002%s\002:"), pattern.c_str());
    124 
    125 		std::vector<Anope::string> replies;
    126 		list.Process(replies);
    127 
    128 		for (unsigned i = 0; i < replies.size(); ++i)
    129 			source.Reply(replies[i]);
    130 
    131 		source.Reply(_("End of list - %d/%d matches shown."), nnicks > listmax ? listmax : nnicks, nnicks);
    132 		return;
    133 	}
    134 
    135 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    136 	{
    137 		this->SendSyntax(source);
    138 		source.Reply(" ");
    139 		source.Reply(_("Lists all registered nicknames which match the given\n"
    140 				"pattern, in \037nick!user@host\037 format.  Nicks with the \002PRIVATE\002\n"
    141 				"option set will only be displayed to Services Operators with the\n"
    142 				"proper access.  Nicks with the \002NOEXPIRE\002 option set will have\n"
    143 				"a \002!\002 prefixed to the nickname for Services Operators to see.\n"
    144 				" \n"
    145 				"Note that a preceding '#' specifies a range.\n"
    146 				" \n"
    147 				"If the SUSPENDED, UNCONFIRMED or NOEXPIRE options are given, only\n"
    148 				"nicks which, respectively, are SUSPENDED, UNCONFIRMED or have the\n"
    149 				"NOEXPIRE flag set will be displayed. If multiple options are\n"
    150 				"given, all nicks matching at least one option will be displayed.\n"
    151 				"Note that these options are limited to \037Services Operators\037.\n"
    152 				" \n"
    153 				"Examples:\n"
    154 				" \n"
    155 				"    \002LIST *!joeuser@foo.com\002\n"
    156 				"        Lists all registered nicks owned by joeuser@foo.com.\n"
    157 				" \n"
    158 				"    \002LIST *Bot*!*@*\002\n"
    159 				"        Lists all registered nicks with \002Bot\002 in their\n"
    160 				"        names (case insensitive).\n"
    161 				" \n"
    162 				"    \002LIST * NOEXPIRE\002\n"
    163 				"        Lists all registered nicks which have been set to not expire.\n"
    164 				" \n"
    165 				"    \002LIST #51-100\002\n"
    166 				"        Lists all registered nicks within the given range (51-100)."));
    167 
    168 		const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
    169 		if (!regexengine.empty())
    170 		{
    171 			source.Reply(" ");
    172 			source.Reply(_("Regex matches are also supported using the %s engine.\n"
    173 					"Enclose your pattern in // if this is desired."), regexengine.c_str());
    174 		}
    175 
    176 		return true;
    177 	}
    178 };
    179 
    180 
    181 class CommandNSSetPrivate : public Command
    182 {
    183  public:
    184 	CommandNSSetPrivate(Module *creator, const Anope::string &sname = "nickserv/set/private", size_t min = 1) : Command(creator, sname, min, min + 1)
    185 	{
    186 		this->SetDesc(_("Prevent the nickname from appearing in the LIST command"));
    187 		this->SetSyntax("{ON | OFF}");
    188 	}
    189 
    190 	void Run(CommandSource &source, const Anope::string &user, const Anope::string &param)
    191 	{
    192 		if (Anope::ReadOnly)
    193 		{
    194 			source.Reply(READ_ONLY_MODE);
    195 			return;
    196 		}
    197 
    198 		const NickAlias *na = NickAlias::Find(user);
    199 		if (!na)
    200 		{
    201 			source.Reply(NICK_X_NOT_REGISTERED, user.c_str());
    202 			return;
    203 		}
    204 		NickCore *nc = na->nc;
    205 
    206 		EventReturn MOD_RESULT;
    207 		FOREACH_RESULT(OnSetNickOption, MOD_RESULT, (source, this, nc, param));
    208 		if (MOD_RESULT == EVENT_STOP)
    209 			return;
    210 
    211 		if (param.equals_ci("ON"))
    212 		{
    213 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to enable private for " << nc->display;
    214 			nc->Extend<bool>("NS_PRIVATE");
    215 			source.Reply(_("Private option is now \002on\002 for \002%s\002."), nc->display.c_str());
    216 		}
    217 		else if (param.equals_ci("OFF"))
    218 		{
    219 			Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to disable private for " << nc->display;
    220 			nc->Shrink<bool>("NS_PRIVATE");
    221 			source.Reply(_("Private option is now \002off\002 for \002%s\002."), nc->display.c_str());
    222 		}
    223 		else
    224 			this->OnSyntaxError(source, "PRIVATE");
    225 	}
    226 
    227 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    228 	{
    229 		this->Run(source, source.nc->display, params[0]);
    230 	}
    231 
    232 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    233 	{
    234 		this->SendSyntax(source);
    235 		source.Reply(" ");
    236 		source.Reply(_("Turns %s's privacy option on or off for your nick.\n"
    237 				"With \002PRIVATE\002 set, your nickname will not appear in\n"
    238 				"nickname lists generated with %s's \002LIST\002 command.\n"
    239 				"(However, anyone who knows your nickname can still get\n"
    240 				"information on it using the \002INFO\002 command.)"),
    241 				source.service->nick.c_str(), source.service->nick.c_str());
    242 		return true;
    243 	}
    244 };
    245 
    246 class CommandNSSASetPrivate : public CommandNSSetPrivate
    247 {
    248  public:
    249 	CommandNSSASetPrivate(Module *creator) : CommandNSSetPrivate(creator, "nickserv/saset/private", 2)
    250 	{
    251 		this->ClearSyntax();
    252 		this->SetSyntax(_("\037nickname\037 {ON | OFF}"));
    253 	}
    254 
    255 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
    256 	{
    257 		this->Run(source, params[0], params[1]);
    258 	}
    259 
    260 	bool OnHelp(CommandSource &source, const Anope::string &) anope_override
    261 	{
    262 		this->SendSyntax(source);
    263 		source.Reply(" ");
    264 		source.Reply(_("Turns %s's privacy option on or off for the nick.\n"
    265 				"With \002PRIVATE\002 set, the nickname will not appear in\n"
    266 				"nickname lists generated with %s's \002LIST\002 command.\n"
    267 				"(However, anyone who knows the nickname can still get\n"
    268 				"information on it using the \002INFO\002 command.)"),
    269 				source.service->nick.c_str(), source.service->nick.c_str());
    270 		return true;
    271 	}
    272 };
    273 
    274 
    275 class NSList : public Module
    276 {
    277 	CommandNSList commandnslist;
    278 
    279 	CommandNSSetPrivate commandnssetprivate;
    280 	CommandNSSASetPrivate commandnssasetprivate;
    281 
    282 	SerializableExtensibleItem<bool> priv;
    283 
    284  public:
    285 	NSList(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
    286 		commandnslist(this), commandnssetprivate(this), commandnssasetprivate(this),
    287 		priv(this, "NS_PRIVATE")
    288 	{
    289 	}
    290 
    291 	void OnNickInfo(CommandSource &source, NickAlias *na, InfoFormatter &info, bool show_all) anope_override
    292 	{
    293 		if (!show_all)
    294 			return;
    295 
    296 		if (priv.HasExt(na->nc))
    297 			info.AddOption(_("Private"));
    298 	}
    299 };
    300 
    301 MODULE_INIT(NSList)