Anope IRC Services  Version 2.0
os_akill.cpp
Go to the documentation of this file.
1 /* OperServ core functions
2  *
3  * (C) 2003-2014 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 static ServiceReference<XLineManager> akills("XLineManager", "xlinemanager/sgline");
15 
17 {
19  unsigned deleted;
21  public:
22  AkillDelCallback(CommandSource &_source, const Anope::string &numlist, Command *c) : NumberList(numlist, true), source(_source), deleted(0), cmd(c)
23  {
24  }
25 
27  {
28  if (!deleted)
29  source.Reply(_("No matching entries on the AKILL list."));
30  else if (deleted == 1)
31  source.Reply(_("Deleted 1 entry from the AKILL list."));
32  else
33  source.Reply(_("Deleted %d entries from the AKILL list."), deleted);
34  }
35 
36  void HandleNumber(unsigned number) anope_override
37  {
38  if (!number)
39  return;
40 
41  XLine *x = akills->GetEntry(number - 1);
42 
43  if (!x)
44  return;
45 
46  Log(LOG_ADMIN, source, cmd) << "to remove " << x->mask << " from the list";
47 
48  ++deleted;
49  DoDel(source, x);
50  }
51 
52  static void DoDel(CommandSource &source, XLine *x)
53  {
54  akills->DelXLine(x);
55  }
56 };
57 
58 class CommandOSAKill : public Command
59 {
60  private:
61  void DoAdd(CommandSource &source, const std::vector<Anope::string> &params)
62  {
63  Anope::string expiry, mask;
64 
65  if (params.size() < 2)
66  {
67  this->OnSyntaxError(source, "ADD");
68  return;
69  }
70 
71  spacesepstream sep(params[1]);
72  sep.GetToken(mask);
73 
74  if (mask[0] == '+')
75  {
76  expiry = mask;
77  sep.GetToken(mask);
78  }
79 
80  time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("autokillexpiry", "30d");
81  /* If the expiry given does not contain a final letter, it's in days,
82  * said the doc. Ah well.
83  */
84  if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
85  expires *= 86400;
86  /* Do not allow less than a minute expiry time */
87  if (expires && expires < 60)
88  {
89  source.Reply(BAD_EXPIRY_TIME);
90  return;
91  }
92  else if (expires > 0)
93  expires += Anope::CurTime;
94 
95  if (sep.StreamEnd())
96  {
97  this->OnSyntaxError(source, "ADD");
98  return;
99  }
100 
101  Anope::string reason;
102  if (mask.find('#') != Anope::string::npos)
103  {
104  Anope::string remaining = sep.GetRemaining();
105 
106  size_t co = remaining[0] == ':' ? 0 : remaining.rfind(" :");
107  if (co == Anope::string::npos)
108  {
109  this->OnSyntaxError(source, "ADD");
110  return;
111  }
112 
113  if (co != 0)
114  ++co;
115 
116  reason = remaining.substr(co + 1);
117  mask += " " + remaining.substr(0, co);
118  mask.trim();
119  }
120  else
121  reason = sep.GetRemaining();
122 
123  if (mask[0] == '/' && mask[mask.length() - 1] == '/')
124  {
125  const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
126 
127  if (regexengine.empty())
128  {
129  source.Reply(_("Regex is disabled."));
130  return;
131  }
132 
133  ServiceReference<RegexProvider> provider("Regex", regexengine);
134  if (!provider)
135  {
136  source.Reply(_("Unable to find regex engine %s."), regexengine.c_str());
137  return;
138  }
139 
140  try
141  {
142  Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
143  delete provider->Compile(stripped_mask);
144  }
145  catch (const RegexException &ex)
146  {
147  source.Reply("%s", ex.GetReason().c_str());
148  return;
149  }
150  }
151 
152  User *targ = User::Find(mask, true);
153  if (targ)
154  mask = "*@" + targ->host;
155 
156  if (!akills->CanAdd(source, mask, expires, reason))
157  return;
158  else if (mask.find_first_not_of("/~@.*?") == Anope::string::npos)
159  {
160  source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
161  return;
162  }
163  else if (mask.find('@') == Anope::string::npos)
164  {
165  source.Reply(BAD_USERHOST_MASK);
166  return;
167  }
168 
169  if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty())
170  reason = "[" + source.GetNick() + "] " + reason;
171 
172  XLine *x = new XLine(mask, source.GetNick(), expires, reason);
173  if (Config->GetModule("operserv")->Get<bool>("akillids"))
175 
176  unsigned int affected = 0;
177  for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
178  if (akills->Check(it->second, x))
179  ++affected;
180  float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
181 
182  if (percent > 95)
183  {
184  source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
185  Log(LOG_ADMIN, source, this) << "tried to akill " << percent << "% of the network (" << affected << " users)";
186  delete x;
187  return;
188  }
189 
190  EventReturn MOD_RESULT;
191  FOREACH_RESULT(OnAddXLine, MOD_RESULT, (source, x, akills));
192  if (MOD_RESULT == EVENT_STOP)
193  {
194  delete x;
195  return;
196  }
197 
198  akills->AddXLine(x);
199  if (Config->GetModule("operserv")->Get<bool>("akillonadd"))
200  akills->Send(NULL, x);
201 
202  source.Reply(_("\002%s\002 added to the AKILL list."), mask.c_str());
203 
204  Log(LOG_ADMIN, source, this) << "on " << mask << " (" << x->reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
205  if (Anope::ReadOnly)
206  source.Reply(READ_ONLY_MODE);
207  }
208 
209  void DoDel(CommandSource &source, const std::vector<Anope::string> &params)
210  {
211  const Anope::string &mask = params.size() > 1 ? params[1] : "";
212 
213  if (mask.empty())
214  {
215  this->OnSyntaxError(source, "DEL");
216  return;
217  }
218 
219  if (akills->GetList().empty())
220  {
221  source.Reply(_("AKILL list is empty."));
222  return;
223  }
224 
225  if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
226  {
227  AkillDelCallback list(source, mask, this);
228  list.Process();
229  }
230  else
231  {
232  XLine *x = akills->HasEntry(mask);
233 
234  if (!x)
235  {
236  source.Reply(_("\002%s\002 not found on the AKILL list."), mask.c_str());
237  return;
238  }
239 
240  do
241  {
242  FOREACH_MOD(OnDelXLine, (source, x, akills));
243 
244  Log(LOG_ADMIN, source, this) << "to remove " << x->mask << " from the list";
245  source.Reply(_("\002%s\002 deleted from the AKILL list."), x->mask.c_str());
246  AkillDelCallback::DoDel(source, x);
247  }
248  while ((x = akills->HasEntry(mask)));
249 
250  }
251 
252  if (Anope::ReadOnly)
253  source.Reply(READ_ONLY_MODE);
254 
255  return;
256  }
257 
258  void ProcessList(CommandSource &source, const std::vector<Anope::string> &params, ListFormatter &list)
259  {
260  const Anope::string &mask = params.size() > 1 ? params[1] : "";
261 
262  if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
263  {
264  class ListCallback : public NumberList
265  {
266  CommandSource &source;
267  ListFormatter &list;
268  public:
269  ListCallback(CommandSource &_source, ListFormatter &_list, const Anope::string &numstr) : NumberList(numstr, false), source(_source), list(_list)
270  {
271  }
272 
273  void HandleNumber(unsigned number) anope_override
274  {
275  if (!number)
276  return;
277 
278  const XLine *x = akills->GetEntry(number - 1);
279 
280  if (!x)
281  return;
282 
284  entry["Number"] = stringify(number);
285  entry["Mask"] = x->mask;
286  entry["Creator"] = x->by;
287  entry["Created"] = Anope::strftime(x->created, NULL, true);
288  entry["Expires"] = Anope::Expires(x->expires, source.nc);
289  entry["Reason"] = x->reason;
290  this->list.AddEntry(entry);
291  }
292  }
293  nl_list(source, list, mask);
294  nl_list.Process();
295  }
296  else
297  {
298  for (unsigned i = 0, end = akills->GetCount(); i < end; ++i)
299  {
300  const XLine *x = akills->GetEntry(i);
301 
302  if (mask.empty() || mask.equals_ci(x->mask) || mask == x->id || Anope::Match(x->mask, mask, false, true))
303  {
305  entry["Number"] = stringify(i + 1);
306  entry["Mask"] = x->mask;
307  entry["Creator"] = x->by;
308  entry["Created"] = Anope::strftime(x->created, NULL, true);
309  entry["Expires"] = Anope::Expires(x->expires, source.nc);
310  entry["Reason"] = x->reason;
311  list.AddEntry(entry);
312  }
313  }
314  }
315 
316  if (list.IsEmpty())
317  source.Reply(_("No matching entries on the AKILL list."));
318  else
319  {
320  source.Reply(_("Current AKILL list:"));
321 
322  std::vector<Anope::string> replies;
323  list.Process(replies);
324 
325  for (unsigned i = 0; i < replies.size(); ++i)
326  source.Reply(replies[i]);
327 
328  source.Reply(_("End of AKILL list."));
329  }
330  }
331 
332  void DoList(CommandSource &source, const std::vector<Anope::string> &params)
333  {
334  if (akills->GetList().empty())
335  {
336  source.Reply(_("AKILL list is empty."));
337  return;
338  }
339 
340  ListFormatter list(source.GetAccount());
341  list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason"));
342 
343  this->ProcessList(source, params, list);
344  }
345 
346  void DoView(CommandSource &source, const std::vector<Anope::string> &params)
347  {
348  if (akills->GetList().empty())
349  {
350  source.Reply(_("AKILL list is empty."));
351  return;
352  }
353 
354  ListFormatter list(source.GetAccount());
355  list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Creator")).AddColumn(_("Created")).AddColumn(_("Expires")).AddColumn(_("Reason"));
356 
357  this->ProcessList(source, params, list);
358  }
359 
360  void DoClear(CommandSource &source)
361  {
362 
363  for (unsigned i = akills->GetCount(); i > 0; --i)
364  {
365  XLine *x = akills->GetEntry(i - 1);
366  FOREACH_MOD(OnDelXLine, (source, x, akills));
367  akills->DelXLine(x);
368  }
369 
370  Log(LOG_ADMIN, source, this) << "to CLEAR the list";
371  source.Reply(_("The AKILL list has been cleared."));
372 
373  if (Anope::ReadOnly)
374  source.Reply(READ_ONLY_MODE);
375  }
376  public:
377  CommandOSAKill(Module *creator) : Command(creator, "operserv/akill", 1, 2)
378  {
379  this->SetDesc(_("Manipulate the AKILL list"));
380  this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037 \037reason\037"));
381  this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
382  this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
383  this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
384  this->SetSyntax("CLEAR");
385  }
386 
387  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
388  {
389  const Anope::string &cmd = params[0];
390 
391  if (!akills)
392  return;
393 
394  if (cmd.equals_ci("ADD"))
395  return this->DoAdd(source, params);
396  else if (cmd.equals_ci("DEL"))
397  return this->DoDel(source, params);
398  else if (cmd.equals_ci("LIST"))
399  return this->DoList(source, params);
400  else if (cmd.equals_ci("VIEW"))
401  return this->DoView(source, params);
402  else if (cmd.equals_ci("CLEAR"))
403  return this->DoClear(source);
404  else
405  this->OnSyntaxError(source, "");
406 
407  return;
408  }
409 
410  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
411  {
412  this->SendSyntax(source);
413  source.Reply(" ");
414  source.Reply(_("Allows Services Operators to manipulate the AKILL list. If\n"
415  "a user matching an AKILL mask attempts to connect, Services\n"
416  "will issue a KILL for that user and, on supported server\n"
417  "types, will instruct all servers to add a ban for the mask\n"
418  "which the user matched.\n"
419  " \n"
420  "\002AKILL ADD\002 adds the given mask to the AKILL\n"
421  "list for the given reason, which \002must\002 be given.\n"
422  "Mask should be in the format of nick!user@host#real name,\n"
423  "though all that is required is user@host. If a real name is specified,\n"
424  "the reason must be prepended with a :.\n"
425  "\037expiry\037 is specified as an integer followed by one of \037d\037\n"
426  "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
427  "\0371h30m\037) are not permitted. If a unit specifier is not\n"
428  "included, the default is days (so \037+30\037 by itself means 30\n"
429  "days). To add an AKILL which does not expire, use \037+0\037. If the\n"
430  "usermask to be added starts with a \037+\037, an expiry time must\n"
431  "be given, even if it is the same as the default. The\n"
432  "current AKILL default expiry time can be found with the\n"
433  "\002STATS AKILL\002 command."));
434  const Anope::string &regexengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine");
435  if (!regexengine.empty())
436  {
437  source.Reply(" ");
438  source.Reply(_("Regex matches are also supported using the %s engine.\n"
439  "Enclose your mask in // if this is desired."), regexengine.c_str());
440  }
441  source.Reply(_(
442  " \n"
443  "The \002AKILL DEL\002 command removes the given mask from the\n"
444  "AKILL list if it is present. If a list of entry numbers is\n"
445  "given, those entries are deleted. (See the example for LIST\n"
446  "below.)\n"
447  " \n"
448  "The \002AKILL LIST\002 command displays the AKILL list.\n"
449  "If a wildcard mask is given, only those entries matching the\n"
450  "mask are displayed. If a list of entry numbers is given,\n"
451  "only those entries are shown; for example:\n"
452  " \002AKILL LIST 2-5,7-9\002\n"
453  " Lists AKILL entries numbered 2 through 5 and 7\n"
454  " through 9.\n"
455  " \n"
456  "\002AKILL VIEW\002 is a more verbose version of \002AKILL LIST\002, and\n"
457  "will show who added an AKILL, the date it was added, and when\n"
458  "it expires, as well as the user@host/ip mask and reason.\n"
459  " \n"
460  "\002AKILL CLEAR\002 clears all entries of the AKILL list."));
461  return true;
462  }
463 };
464 
465 class OSAKill : public Module
466 {
468 
469  public:
470  OSAKill(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
471  commandosakill(this)
472  {
473 
474  }
475 };
476 
CoreExport bool ReadOnly
Definition: main.cpp:28
Anope::string reason
Definition: xline.h:29
Anope::string mask
Definition: xline.h:24
void DoList(CommandSource &source, const std::vector< Anope::string > &params)
Definition: os_akill.cpp:332
#define BAD_USERHOST_MASK
Definition: language.h:68
void Process(std::vector< Anope::string > &)
Definition: misc.cpp:144
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: os_akill.cpp:410
void DoView(CommandSource &source, const std::vector< Anope::string > &params)
Definition: os_akill.cpp:346
Definition: users.h:34
AkillDelCallback(CommandSource &_source, const Anope::string &numlist, Command *c)
Definition: os_akill.cpp:22
void AddEntry(const ListEntry &entry)
Definition: misc.cpp:134
size_type rfind(const string &_str, size_type pos=npos) const
Definition: anope.h:197
#define READ_ONLY_MODE
Definition: language.h:71
void DoAdd(CommandSource &source, const std::vector< Anope::string > &params)
Definition: os_akill.cpp:61
#define FOREACH_RESULT(ename, ret, args)
Definition: modules.h:95
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
CoreExport time_t CurTime
Definition: main.cpp:41
const Anope::string & GetNick() const
Definition: command.cpp:26
#define FOREACH_MOD(ename, args)
Definition: modules.h:62
void ProcessList(CommandSource &source, const std::vector< Anope::string > &params, ListFormatter &list)
Definition: os_akill.cpp:258
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
void DoDel(CommandSource &source, const std::vector< Anope::string > &params)
Definition: os_akill.cpp:209
std::map< Anope::string, Anope::string > ListEntry
Definition: lists.h:68
size_type length() const
Definition: anope.h:131
Definition: Config.cs:26
CoreExport bool Match(const string &str, const string &mask, bool case_sensitive=false, bool use_regex=false)
Definition: misc.cpp:407
virtual void OnSyntaxError(CommandSource &source, const Anope::string &subcommand)
Definition: command.cpp:191
static const size_type npos
Definition: anope.h:44
bool IsEmpty() const
Definition: misc.cpp:139
CoreExport time_t DoTime(const Anope::string &s)
Definition: misc.cpp:275
void Reply(const char *message,...)
Definition: command.cpp:96
void DoClear(CommandSource &source)
Definition: os_akill.cpp:360
unsigned deleted
Definition: os_akill.cpp:19
Anope::string by
Definition: xline.h:26
time_t expires
Definition: xline.h:28
string & trim(const Anope::string &what=" \t\r\n")
Definition: anope.h:177
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
Anope::string id
Definition: xline.h:31
Anope::string host
Definition: users.h:65
virtual Regex * Compile(const Anope::string &)=0
const Anope::string GetRemaining()
Definition: hashcomp.cpp:156
EventReturn
Definition: modules.h:129
#define MODULE_INIT(x)
Definition: modules.h:45
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
void Process()
Definition: misc.cpp:98
static ServiceReference< XLineManager > akills("XLineManager","xlinemanager/sgline")
size_type find_first_not_of(const string &_str, size_type pos=0) const
Definition: anope.h:205
CommandOSAKill commandosakill
Definition: os_akill.cpp:467
Anope::string stringify(const T &x)
Definition: anope.h:710
Command * cmd
Definition: os_akill.cpp:20
OSAKill(const Anope::string &modname, const Anope::string &creator)
Definition: os_akill.cpp:470
CommandOSAKill(Module *creator)
Definition: os_akill.cpp:377
static User * Find(const Anope::string &name, bool nick_only=false)
Definition: users.cpp:815
#define USERHOST_MASK_TOO_WIDE
Definition: language.h:70
Definition: xline.h:18
bool GetToken(Anope::string &token)
Definition: hashcomp.cpp:99
Reference< NickCore > nc
Definition: commands.h:61
virtual const Anope::string & GetReason() const
Definition: anope.h:672
CoreExport user_map UserListByNick
Definition: users.cpp:28
void SendSyntax(CommandSource &)
Definition: command.cpp:145
NickCore * GetAccount()
Definition: command.cpp:36
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
CoreExport Anope::string Duration(time_t seconds, const NickCore *nc=NULL)
Definition: misc.cpp:315
CommandSource & source
Definition: os_akill.cpp:18
#define BAD_EXPIRY_TIME
Definition: language.h:69
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: os_akill.cpp:387
CoreExport Anope::string strftime(time_t t, const NickCore *nc=NULL, bool short_output=false)
Definition: misc.cpp:356
bool StreamEnd()
Definition: hashcomp.cpp:161
static Anope::string GenerateUID()
Definition: xline.cpp:234
void HandleNumber(unsigned number) anope_override
Definition: os_akill.cpp:36
time_t created
Definition: xline.h:27
static void DoDel(CommandSource &source, XLine *x)
Definition: os_akill.cpp:52
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
#define _(x)
Definition: services.h:50
CoreExport Anope::string Expires(time_t seconds, const NickCore *nc=NULL)
Definition: misc.cpp:371
ListFormatter & AddColumn(const Anope::string &name)
Definition: misc.cpp:128