Anope IRC Services  Version 2.0
cs_access.cpp
Go to the documentation of this file.
1 /* ChanServ 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 std::map<Anope::string, int16_t, ci::less> defaultLevels;
15 
16 static inline void reset_levels(ChannelInfo *ci)
17 {
18  ci->ClearLevels();
19  for (std::map<Anope::string, int16_t, ci::less>::iterator it = defaultLevels.begin(), it_end = defaultLevels.end(); it != it_end; ++it)
20  ci->SetLevel(it->first, it->second);
21 }
22 
24 {
25  public:
26  int level;
27 
29  {
30  }
31 
33  {
34  return this->ci->GetLevel(name) != ACCESS_INVALID && this->level >= this->ci->GetLevel(name);
35  }
36 
38  {
39  return stringify(this->level);
40  }
41 
43  {
44  try
45  {
46  this->level = convertTo<int>(data);
47  }
48  catch (const ConvertException &)
49  {
50  }
51  }
52 
53  bool operator>(const ChanAccess &other) const anope_override
54  {
55  if (this->provider != other.provider)
56  return ChanAccess::operator>(other);
57  else
58  return this->level > anope_dynamic_static_cast<const AccessChanAccess *>(&other)->level;
59  }
60 
61  bool operator<(const ChanAccess &other) const anope_override
62  {
63  if (this->provider != other.provider)
64  return ChanAccess::operator<(other);
65  else
66  return this->level < anope_dynamic_static_cast<const AccessChanAccess *>(&other)->level;
67  }
68 };
69 
71 {
72  public:
74 
75  AccessAccessProvider(Module *o) : AccessProvider(o, "access/access")
76  {
77  me = this;
78  }
79 
81  {
82  return new AccessChanAccess(this);
83  }
84 };
86 
87 class CommandCSAccess : public Command
88 {
89  void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
90  {
91  Anope::string mask = params[2];
92  Privilege *p = NULL;
93  int level = ACCESS_INVALID;
94 
95  try
96  {
97  level = convertTo<int>(params[3]);
98  }
99  catch (const ConvertException &)
100  {
101  p = PrivilegeManager::FindPrivilege(params[3]);
102  if (p != NULL && defaultLevels[p->name])
103  level = defaultLevels[p->name];
104  }
105 
106  if (!level)
107  {
108  source.Reply(_("Access level must be non-zero."));
109  return;
110  }
111  else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER)
112  {
114  return;
115  }
116 
117  AccessGroup u_access = source.AccessFor(ci);
118  const ChanAccess *highest = u_access.Highest();
119 
120  AccessChanAccess tmp_access(AccessAccessProvider::me);
121  tmp_access.ci = ci;
122  tmp_access.level = level;
123 
124  bool override = false;
125 
126  if ((!highest || *highest <= tmp_access) && !u_access.founder)
127  {
128  if (source.HasPriv("chanserv/access/modify"))
129  override = true;
130  else
131  {
132  source.Reply(ACCESS_DENIED);
133  return;
134  }
135  }
136 
137  if (IRCD->IsChannelValid(mask))
138  {
139  if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
140  {
141  source.Reply(_("Channels may not be on access lists."));
142  return;
143  }
144 
145  ChannelInfo *targ_ci = ChannelInfo::Find(mask);
146  if (targ_ci == NULL)
147  {
148  source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str());
149  return;
150  }
151  else if (ci == targ_ci)
152  {
153  source.Reply(_("You can't add a channel to its own access list."));
154  return;
155  }
156 
157  mask = targ_ci->name;
158  }
159  else
160  {
161  const NickAlias *na = NickAlias::Find(mask);
162  if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
163  {
164  source.Reply(_("Masks and unregistered users may not be on access lists."));
165  return;
166  }
167  else if (mask.find_first_of("!*@") == Anope::string::npos && !na)
168  {
169  User *targ = User::Find(mask, true);
170  if (targ != NULL)
171  mask = "*!*@" + targ->GetDisplayedHost();
172  else
173  {
174  source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
175  return;
176  }
177  }
178  }
179 
180  for (unsigned i = ci->GetAccessCount(); i > 0; --i)
181  {
182  const ChanAccess *access = ci->GetAccess(i - 1);
183  if (mask.equals_ci(access->Mask()))
184  {
185  /* Don't allow lowering from a level >= u_level */
186  if ((!highest || *access >= *highest) && !u_access.founder && !source.HasPriv("chanserv/access/modify"))
187  {
188  source.Reply(ACCESS_DENIED);
189  return;
190  }
191  delete ci->EraseAccess(i - 1);
192  break;
193  }
194  }
195 
196  unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
197  if (access_max && ci->GetDeepAccessCount() >= access_max)
198  {
199  source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
200  return;
201  }
202 
203  ServiceReference<AccessProvider> provider("AccessProvider", "access/access");
204  if (!provider)
205  return;
207  access->SetMask(mask, ci);
208  access->creator = source.GetNick();
209  access->level = level;
210  access->last_seen = 0;
211  access->created = Anope::CurTime;
212  ci->AddAccess(access);
213 
214  FOREACH_MOD(OnAccessAdd, (ci, source, access));
215 
216  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to add " << mask << " with level " << level;
217  if (p != NULL)
218  source.Reply(_("\002%s\002 added to %s access list at privilege %s (level %d)"), access->Mask().c_str(), ci->name.c_str(), p->name.c_str(), level);
219  else
220  source.Reply(_("\002%s\002 added to %s access list at level \002%d\002."), access->Mask().c_str(), ci->name.c_str(), level);
221  }
222 
223  void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
224  {
225  Anope::string mask = params[2];
226 
227  if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickAlias::Find(mask))
228  {
229  User *targ = User::Find(mask, true);
230  if (targ != NULL)
231  mask = "*!*@" + targ->GetDisplayedHost();
232  else
233  {
234  source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
235  return;
236  }
237  }
238 
239  if (!ci->GetAccessCount())
240  source.Reply(_("%s access list is empty."), ci->name.c_str());
241  else if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
242  {
243  class AccessDelCallback : public NumberList
244  {
245  CommandSource &source;
246  ChannelInfo *ci;
247  Command *c;
248  unsigned deleted;
249  Anope::string Nicks;
250  bool denied;
251  bool override;
252  public:
253  AccessDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &numlist) : NumberList(numlist, true), source(_source), ci(_ci), c(_c), deleted(0), denied(false), override(false)
254  {
255  if (!source.AccessFor(ci).HasPriv("ACCESS_CHANGE") && source.HasPriv("chanserv/access/modify"))
256  this->override = true;
257  }
258 
259  ~AccessDelCallback()
260  {
261  if (denied && !deleted)
262  source.Reply(ACCESS_DENIED);
263  else if (!deleted)
264  source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
265  else
266  {
267  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "to delete " << Nicks;
268 
269  if (deleted == 1)
270  source.Reply(_("Deleted 1 entry from %s access list."), ci->name.c_str());
271  else
272  source.Reply(_("Deleted %d entries from %s access list."), deleted, ci->name.c_str());
273  }
274  }
275 
276  void HandleNumber(unsigned Number) anope_override
277  {
278  if (!Number || Number > ci->GetAccessCount())
279  return;
280 
281  ChanAccess *access = ci->GetAccess(Number - 1);
282 
283  AccessGroup ag = source.AccessFor(ci);
284  const ChanAccess *u_highest = ag.Highest();
285 
286  if ((!u_highest || *u_highest <= *access) && !ag.founder && !this->override && access->GetAccount() != source.nc)
287  {
288  denied = true;
289  return;
290  }
291 
292  ++deleted;
293  if (!Nicks.empty())
294  Nicks += ", " + access->Mask();
295  else
296  Nicks = access->Mask();
297 
298  ci->EraseAccess(Number - 1);
299 
300  FOREACH_MOD(OnAccessDel, (ci, source, access));
301  delete access;
302  }
303  }
304  delcallback(source, ci, this, mask);
305  delcallback.Process();
306  }
307  else
308  {
309  AccessGroup u_access = source.AccessFor(ci);
310  const ChanAccess *highest = u_access.Highest();
311 
312  for (unsigned i = ci->GetAccessCount(); i > 0; --i)
313  {
314  ChanAccess *access = ci->GetAccess(i - 1);
315  if (mask.equals_ci(access->Mask()))
316  {
317  if (access->GetAccount() != source.nc && !u_access.founder && (!highest || *highest <= *access) && !source.HasPriv("chanserv/access/modify"))
318  source.Reply(ACCESS_DENIED);
319  else
320  {
321  source.Reply(_("\002%s\002 deleted from %s access list."), access->Mask().c_str(), ci->name.c_str());
322  bool override = !u_access.founder && !u_access.HasPriv("ACCESS_CHANGE") && access->GetAccount() != source.nc;
323  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->Mask();
324 
325  ci->EraseAccess(i - 1);
326  FOREACH_MOD(OnAccessDel, (ci, source, access));
327  delete access;
328  }
329  return;
330  }
331  }
332 
333  source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
334  }
335 
336  return;
337  }
338 
339  void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params, ListFormatter &list)
340  {
341  const Anope::string &nick = params.size() > 2 ? params[2] : "";
342 
343  if (!ci->GetAccessCount())
344  source.Reply(_("%s access list is empty."), ci->name.c_str());
345  else if (!nick.empty() && nick.find_first_not_of("1234567890,-") == Anope::string::npos)
346  {
347  class AccessListCallback : public NumberList
348  {
349  ListFormatter &list;
350  ChannelInfo *ci;
351 
352  public:
353  AccessListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
354  {
355  }
356 
357  void HandleNumber(unsigned number) anope_override
358  {
359  if (!number || number > ci->GetAccessCount())
360  return;
361 
362  const ChanAccess *access = ci->GetAccess(number - 1);
363 
364  Anope::string timebuf;
365  if (ci->c)
366  for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
367  {
369  if (access->Matches(cit->second->user, cit->second->user->Account(), p))
370  timebuf = "Now";
371  }
372  if (timebuf.empty())
373  {
374  if (access->last_seen == 0)
375  timebuf = "Never";
376  else
377  timebuf = Anope::strftime(access->last_seen, NULL, true);
378  }
379 
381  entry["Number"] = stringify(number);
382  entry["Level"] = access->AccessSerialize();
383  entry["Mask"] = access->Mask();
384  entry["By"] = access->creator;
385  entry["Last seen"] = timebuf;
386  this->list.AddEntry(entry);
387  }
388  }
389  nl_list(list, ci, nick);
390  nl_list.Process();
391  }
392  else
393  {
394  for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
395  {
396  const ChanAccess *access = ci->GetAccess(i);
397 
398  if (!nick.empty() && !Anope::Match(access->Mask(), nick))
399  continue;
400 
401  Anope::string timebuf;
402  if (ci->c)
403  for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
404  {
406  if (access->Matches(cit->second->user, cit->second->user->Account(), p))
407  timebuf = "Now";
408  }
409  if (timebuf.empty())
410  {
411  if (access->last_seen == 0)
412  timebuf = "Never";
413  else
414  timebuf = Anope::strftime(access->last_seen, NULL, true);
415  }
416 
418  entry["Number"] = stringify(i + 1);
419  entry["Level"] = access->AccessSerialize();
420  entry["Mask"] = access->Mask();
421  entry["By"] = access->creator;
422  entry["Last seen"] = timebuf;
423  list.AddEntry(entry);
424  }
425  }
426 
427  if (list.IsEmpty())
428  source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
429  else
430  {
431  std::vector<Anope::string> replies;
432  list.Process(replies);
433 
434  source.Reply(_("Access list for %s:"), ci->name.c_str());
435 
436  for (unsigned i = 0; i < replies.size(); ++i)
437  source.Reply(replies[i]);
438 
439  source.Reply(_("End of access list"));
440  }
441 
442  return;
443  }
444 
445  void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
446  {
447  if (!ci->GetAccessCount())
448  {
449  source.Reply(_("%s access list is empty."), ci->name.c_str());
450  return;
451  }
452 
453  ListFormatter list(source.GetAccount());
454  list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask"));
455  this->ProcessList(source, ci, params, list);
456  }
457 
458  void DoView(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
459  {
460  if (!ci->GetAccessCount())
461  {
462  source.Reply(_("%s access list is empty."), ci->name.c_str());
463  return;
464  }
465 
466  ListFormatter list(source.GetAccount());
467  list.AddColumn(_("Number")).AddColumn(_("Level")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Last seen"));
468  this->ProcessList(source, ci, params, list);
469  }
470 
472  {
473  if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
474  source.Reply(ACCESS_DENIED);
475  else
476  {
477  FOREACH_MOD(OnAccessClear, (ci, source));
478 
479  ci->ClearAccess();
480 
481  source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str());
482 
483  bool override = !source.IsFounder(ci);
484  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
485  }
486 
487  return;
488  }
489 
490  public:
491  CommandCSAccess(Module *creator) : Command(creator, "chanserv/access", 2, 4)
492  {
493  this->SetDesc(_("Modify the list of privileged users"));
494  this->SetSyntax(_("\037channel\037 ADD \037mask\037 \037level\037"));
495  this->SetSyntax(_("\037channel\037 DEL {\037mask\037 | \037entry-num\037 | \037list\037}"));
496  this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
497  this->SetSyntax(_("\037channel\037 VIEW [\037mask\037 | \037list\037]"));
498  this->SetSyntax(_("\037channel\037 CLEAR"));
499  }
500 
501  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
502  {
503  const Anope::string &cmd = params[1];
504  const Anope::string &nick = params.size() > 2 ? params[2] : "";
505  const Anope::string &s = params.size() > 3 ? params[3] : "";
506 
507  ChannelInfo *ci = ChannelInfo::Find(params[0]);
508  if (ci == NULL)
509  {
510  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
511  return;
512  }
513 
514  bool is_list = cmd.equals_ci("LIST") || cmd.equals_ci("VIEW");
515  bool is_clear = cmd.equals_ci("CLEAR");
516  bool is_del = cmd.equals_ci("DEL");
517 
518  bool has_access = false;
519  if (source.HasPriv("chanserv/access/modify"))
520  has_access = true;
521  else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
522  has_access = true;
523  else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
524  has_access = true;
525  else if (is_del)
526  {
527  const NickAlias *na = NickAlias::Find(nick);
528  if (na && na->nc == source.GetAccount())
529  has_access = true;
530  }
531 
532  /* If LIST, we don't *require* any parameters, but we can take any.
533  * If DEL, we require a nick and no level.
534  * Else (ADD), we require a level (which implies a nick). */
535  if (is_list || is_clear ? 0 : (cmd.equals_ci("DEL") ? (nick.empty() || !s.empty()) : s.empty()))
536  this->OnSyntaxError(source, cmd);
537  else if (!has_access)
538  source.Reply(ACCESS_DENIED);
539  else if (Anope::ReadOnly && !is_list)
540  source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
541  else if (cmd.equals_ci("ADD"))
542  this->DoAdd(source, ci, params);
543  else if (cmd.equals_ci("DEL"))
544  this->DoDel(source, ci, params);
545  else if (cmd.equals_ci("LIST"))
546  this->DoList(source, ci, params);
547  else if (cmd.equals_ci("VIEW"))
548  this->DoView(source, ci, params);
549  else if (cmd.equals_ci("CLEAR"))
550  this->DoClear(source, ci);
551  else
552  this->OnSyntaxError(source, "");
553 
554  return;
555  }
556 
557  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
558  {
559  this->SendSyntax(source);
560  source.Reply(" ");
561  source.Reply(_("Maintains the \002access list\002 for a channel. The access\n"
562  "list specifies which users are allowed chanop status or\n"
563  "access to %s commands on the channel. Different\n"
564  "user levels allow for access to different subsets of\n"
565  "privileges. Any registered user not on the access list has\n"
566  "a user level of 0, and any unregistered user has a user level\n"
567  "of -1."), source.service->nick.c_str());
568  source.Reply(" ");
569  source.Reply(_("The \002ACCESS ADD\002 command adds the given mask to the\n"
570  "access list with the given user level; if the mask is\n"
571  "already present on the list, its access level is changed to\n"
572  "the level specified in the command. The \037level\037 specified\n"
573  "may be a numerical level or the name of a privilege (eg AUTOOP).\n"
574  "When a user joins the channel the access they receive is from the\n"
575  "highest level entry in the access list."));
576  if (!Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
577  source.Reply(_("The given mask may also be a channel, which will use the\n"
578  "access list from the other channel up to the given \037level\037."));
579  source.Reply(" ");
580  source.Reply(_("The \002ACCESS DEL\002 command removes the given nick from the\n"
581  "access list. If a list of entry numbers is given, those\n"
582  "entries are deleted. (See the example for LIST below.)\n"
583  "You may remove yourself from an access list, even if you\n"
584  "do not have access to modify that list otherwise."));
585  source.Reply(" ");
586  source.Reply(_("The \002ACCESS LIST\002 command displays the access list. If\n"
587  "a wildcard mask is given, only those entries matching the\n"
588  "mask are displayed. If a list of entry numbers is given,\n"
589  "only those entries are shown; for example:\n"
590  " \002ACCESS #channel LIST 2-5,7-9\002\n"
591  " Lists access entries numbered 2 through 5 and\n"
592  " 7 through 9.\n"
593  " \n"
594  "The \002ACCESS VIEW\002 command displays the access list similar\n"
595  "to \002ACCESS LIST\002 but shows the creator and last used time.\n"
596  " \n"
597  "The \002ACCESS CLEAR\002 command clears all entries of the\n"
598  "access list."));
599  source.Reply(" ");
600 
601  BotInfo *bi;
602  Anope::string cmd;
603  if (Command::FindCommandFromService("chanserv/levels", bi, cmd))
604  source.Reply(_("\002User access levels\002 can be seen by using the\n"
605  "\002%s\002 command; type \002%s%s HELP LEVELS\002 for\n"
606  "information."), cmd.c_str(), Config->StrictPrivmsg.c_str(), bi->nick.c_str());
607  return true;
608  }
609 };
610 
611 class CommandCSLevels : public Command
612 {
613  void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
614  {
615  const Anope::string &what = params[2];
616  const Anope::string &lev = params[3];
617 
618  int level;
619 
620  if (lev.equals_ci("FOUNDER"))
621  level = ACCESS_FOUNDER;
622  else
623  {
624  try
625  {
626  level = convertTo<int>(lev);
627  }
628  catch (const ConvertException &)
629  {
630  this->OnSyntaxError(source, "SET");
631  return;
632  }
633  }
634 
635  if (level <= ACCESS_INVALID || level > ACCESS_FOUNDER)
636  source.Reply(_("Level must be between %d and %d inclusive."), ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
637  else
638  {
640  if (p == NULL)
641  source.Reply(_("Setting \002%s\002 not known. Type \002%s%s HELP LEVELS\002 for a list of valid settings."), what.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str());
642  else
643  {
644  bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
645  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << p->name << " to level " << level;
646 
647  ci->SetLevel(p->name, level);
648  FOREACH_MOD(OnLevelChange, (source, ci, p->name, level));
649 
650  if (level == ACCESS_FOUNDER)
651  source.Reply(_("Level for %s on channel %s changed to founder only."), p->name.c_str(), ci->name.c_str());
652  else
653  source.Reply(_("Level for \002%s\002 on channel %s changed to \002%d\002."), p->name.c_str(), ci->name.c_str(), level);
654  }
655  }
656  }
657 
658  void DoDisable(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
659  {
660  const Anope::string &what = params[2];
661 
662  /* Don't allow disabling of the founder level. It would be hard to change it back if you dont have access to use this command */
663  if (what.equals_ci("FOUNDER"))
664  {
665  source.Reply(_("You can not disable the founder privilege because it would be impossible to reenable it at a later time."));
666  return;
667  }
668 
670  if (p != NULL)
671  {
672  bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
673  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to disable " << p->name;
674 
675  ci->SetLevel(p->name, ACCESS_INVALID);
676  FOREACH_MOD(OnLevelChange, (source, ci, p->name, ACCESS_INVALID));
677 
678  source.Reply(_("\002%s\002 disabled on channel %s."), p->name.c_str(), ci->name.c_str());
679  return;
680  }
681 
682  source.Reply(_("Setting \002%s\002 not known. Type \002%s%s HELP LEVELS\002 for a list of valid settings."), what.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str());
683  }
684 
686  {
687  source.Reply(_("Access level settings for channel %s:"), ci->name.c_str());
688 
689  ListFormatter list(source.GetAccount());
690  list.AddColumn(_("Name")).AddColumn(_("Level"));
691 
692  const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
693 
694  for (unsigned i = 0; i < privs.size(); ++i)
695  {
696  const Privilege &p = privs[i];
697  int16_t j = ci->GetLevel(p.name);
698 
700  entry["Name"] = p.name;
701 
702  if (j == ACCESS_INVALID)
703  entry["Level"] = Language::Translate(source.GetAccount(), _("(disabled)"));
704  else if (j == ACCESS_FOUNDER)
705  entry["Level"] = Language::Translate(source.GetAccount(), _("(founder only)"));
706  else
707  entry["Level"] = stringify(j);
708 
709  list.AddEntry(entry);
710  }
711 
712  std::vector<Anope::string> replies;
713  list.Process(replies);
714 
715  for (unsigned i = 0; i < replies.size(); ++i)
716  source.Reply(replies[i]);
717  }
718 
720  {
721  bool override = !source.AccessFor(ci).HasPriv("FOUNDER");
722  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to reset all levels";
723 
724  reset_levels(ci);
725  FOREACH_MOD(OnLevelChange, (source, ci, "ALL", 0));
726 
727  source.Reply(_("Access levels for \002%s\002 reset to defaults."), ci->name.c_str());
728  return;
729  }
730 
731  public:
732  CommandCSLevels(Module *creator) : Command(creator, "chanserv/levels", 2, 4)
733  {
734  this->SetDesc(_("Redefine the meanings of access levels"));
735  this->SetSyntax(_("\037channel\037 SET \037type\037 \037level\037"));
736  this->SetSyntax(_("\037channel\037 {DIS | DISABLE} \037type\037"));
737  this->SetSyntax(_("\037channel\037 LIST"));
738  this->SetSyntax(_("\037channel\037 RESET"));
739  }
740 
741  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
742  {
743  const Anope::string &cmd = params[1];
744  const Anope::string &what = params.size() > 2 ? params[2] : "";
745  const Anope::string &s = params.size() > 3 ? params[3] : "";
746 
747  ChannelInfo *ci = ChannelInfo::Find(params[0]);
748  if (ci == NULL)
749  {
750  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
751  return;
752  }
753 
754  /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only
755  * one; else, we want none.
756  */
757  if (cmd.equals_ci("SET") ? s.empty() : (cmd.substr(0, 3).equals_ci("DIS") ? (what.empty() || !s.empty()) : !what.empty()))
758  this->OnSyntaxError(source, cmd);
759  else if (!source.AccessFor(ci).HasPriv("FOUNDER") && !source.HasPriv("chanserv/access/modify"))
760  source.Reply(ACCESS_DENIED);
761  else if (Anope::ReadOnly && !cmd.equals_ci("LIST"))
762  source.Reply(READ_ONLY_MODE);
763  else if (cmd.equals_ci("SET"))
764  this->DoSet(source, ci, params);
765  else if (cmd.equals_ci("DIS") || cmd.equals_ci("DISABLE"))
766  this->DoDisable(source, ci, params);
767  else if (cmd.equals_ci("LIST"))
768  this->DoList(source, ci);
769  else if (cmd.equals_ci("RESET"))
770  this->DoReset(source, ci);
771  else
772  this->OnSyntaxError(source, "");
773 
774  return;
775  }
776 
777  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
778  {
779  if (subcommand.equals_ci("DESC"))
780  {
781  source.Reply(_("The following feature/function names are available:"));
782 
783  ListFormatter list(source.GetAccount());
784  list.AddColumn(_("Name")).AddColumn(_("Description"));
785 
786  const std::vector<Privilege> &privs = PrivilegeManager::GetPrivileges();
787  for (unsigned i = 0; i < privs.size(); ++i)
788  {
789  const Privilege &p = privs[i];
791  entry["Name"] = p.name;
792  entry["Description"] = Language::Translate(source.nc, p.desc.c_str());
793  list.AddEntry(entry);
794  }
795 
796  std::vector<Anope::string> replies;
797  list.Process(replies);
798 
799  for (unsigned i = 0; i < replies.size(); ++i)
800  source.Reply(replies[i]);
801  }
802  else
803  {
804  this->SendSyntax(source);
805  source.Reply(" ");
806  source.Reply(_("The \002LEVELS\002 command allows fine control over the meaning of\n"
807  "the numeric access levels used for channels. With this\n"
808  "command, you can define the access level required for most\n"
809  "of %s's functions. (The \002SET FOUNDER\002 and this command\n"
810  "are always restricted to the channel founder.)\n"
811  " \n"
812  "\002LEVELS SET\002 allows the access level for a function or group of\n"
813  "functions to be changed. \002LEVELS DISABLE\002 (or \002DIS\002 for short)\n"
814  "disables an automatic feature or disallows access to a\n"
815  "function by anyone, INCLUDING the founder (although, the founder\n"
816  "can always reenable it). Use \002LEVELS SET founder\002 to make a level\n"
817  "founder only.\n"
818  " \n"
819  "\002LEVELS LIST\002 shows the current levels for each function or\n"
820  "group of functions. \002LEVELS RESET\002 resets the levels to the\n"
821  "default levels of a newly-created channel.\n"
822  " \n"
823  "For a list of the features and functions whose levels can be\n"
824  "set, see \002HELP LEVELS DESC\002."), source.service->nick.c_str());
825  }
826  return true;
827  }
828 };
829 
830 class CSAccess : public Module
831 {
835 
836  public:
837  CSAccess(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
838  accessprovider(this), commandcsaccess(this), commandcslevels(this)
839  {
840  this->SetPermanent(true);
841 
842  }
843 
845  {
846  defaultLevels.clear();
847 
848  for (int i = 0; i < conf->CountBlock("privilege"); ++i)
849  {
850  Configuration::Block *priv = conf->GetBlock("privilege", i);
851 
852  const Anope::string &pname = priv->Get<const Anope::string>("name");
853 
855  if (p == NULL)
856  continue;
857 
858  const Anope::string &value = priv->Get<const Anope::string>("level");
859  if (value.empty())
860  continue;
861  else if (value.equals_ci("founder"))
862  defaultLevels[p->name] = ACCESS_FOUNDER;
863  else if (value.equals_ci("disabled"))
864  defaultLevels[p->name] = ACCESS_INVALID;
865  else
866  defaultLevels[p->name] = priv->Get<int16_t>("level");
867  }
868  }
869 
871  {
872  reset_levels(ci);
873  }
874 
876  {
877  if (group->ci == NULL)
878  return EVENT_CONTINUE;
879  /* Special case. Allows a level of < 0 to match anyone, and a level of 0 to match anyone identified. */
880  int16_t level = group->ci->GetLevel(priv);
881  if (level < 0)
882  return EVENT_ALLOW;
883  else if (level == 0 && group->nc)
884  return EVENT_ALLOW;
885  return EVENT_CONTINUE;
886  }
887 };
888 
Serialize::Reference< NickCore > nc
Definition: account.h:47
Definition: bots.h:24
CoreExport bool ReadOnly
Definition: main.cpp:28
static NickAlias * Find(const Anope::string &nick)
Definition: nickalias.cpp:121
std::pair< Set, Set > Path
Definition: access.h:87
void DoList(CommandSource &source, ChannelInfo *ci)
Definition: cs_access.cpp:685
Definition: hashcomp.h:84
void OnCreateChan(ChannelInfo *ci) anope_override
Definition: cs_access.cpp:870
Anope::string name
Definition: regchannel.h:63
static std::map< Anope::string, int16_t, ci::less > defaultLevels
Definition: cs_access.cpp:14
virtual bool operator<(const ChanAccess &other) const
Definition: access.cpp:324
void AccessUnserialize(const Anope::string &data) anope_override
Definition: cs_access.cpp:42
#define ACCESS_DENIED
Definition: language.h:73
void Process(std::vector< Anope::string > &)
Definition: misc.cpp:144
static bool FindCommandFromService(const Anope::string &command_service, BotInfo *&bi, Anope::string &name)
Definition: command.cpp:287
Definition: users.h:34
void AddEntry(const ListEntry &entry)
Definition: misc.cpp:134
#define NICK_X_NOT_REGISTERED
Definition: language.h:79
Anope::string desc
Definition: access.h:35
Anope::string creator
Definition: access.h:93
bool IsFounder(ChannelInfo *ci)
Definition: command.cpp:51
virtual Anope::string AccessSerialize() const =0
void DoClear(CommandSource &source, ChannelInfo *ci)
Definition: cs_access.cpp:471
unsigned GetDeepAccessCount() const
Definition: regchannel.cpp:479
virtual bool operator>(const ChanAccess &other) const
Definition: access.cpp:307
CommandCSAccess commandcsaccess
Definition: cs_access.cpp:833
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: cs_access.cpp:501
time_t created
Definition: access.h:95
#define READ_ONLY_MODE
Definition: language.h:71
void SetLevel(const Anope::string &priv, int16_t level)
Definition: regchannel.cpp:595
bool founder
Definition: access.h:150
NickCore * GetAccount() const
Definition: access.cpp:204
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
size_type find_first_of(const string &_str, size_type pos=0) const
Definition: anope.h:202
CoreExport time_t CurTime
Definition: main.cpp:41
bool HasPriv(const Anope::string &priv) const
Definition: access.cpp:384
const Anope::string & GetNick() const
Definition: command.cpp:26
void ProcessList(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params, ListFormatter &list)
Definition: cs_access.cpp:339
#define FOREACH_MOD(ename, args)
Definition: modules.h:62
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: cs_access.cpp:777
static Privilege * FindPrivilege(const Anope::string &name)
Definition: access.cpp:105
static ChannelInfo * Find(const Anope::string &name)
Definition: regchannel.cpp:630
bool operator<(const ChanAccess &other) const anope_override
Definition: cs_access.cpp:61
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
std::map< Anope::string, Anope::string > ListEntry
Definition: lists.h:68
Block * GetBlock(const Anope::string &name, int num=0)
Definition: config.cpp:43
Definition: Config.cs:26
Channel * c
Definition: regchannel.h:79
void DoList(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_access.cpp:445
time_t last_seen
Definition: access.h:94
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
ChanAccess * EraseAccess(unsigned index)
Definition: regchannel.cpp:503
static const size_type npos
Definition: anope.h:44
bool IsEmpty() const
Definition: misc.cpp:139
void Reply(const char *message,...)
Definition: command.cpp:96
CommandCSLevels commandcslevels
Definition: cs_access.cpp:834
const Anope::string & GetDisplayedHost() const
Definition: users.cpp:203
static std::vector< Privilege > & GetPrivileges()
Definition: access.cpp:113
const Anope::string & Mask() const
Definition: access.cpp:196
bool HasPriv(const Anope::string &cmd)
Definition: command.cpp:69
Reference< BotInfo > service
Definition: commands.h:67
const ChanAccess * Highest() const
Definition: access.cpp:416
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: cs_access.cpp:741
Serialize::Reference< ChannelInfo > ci
Definition: access.h:92
virtual ChanAccess * Create()=0
unsigned GetAccessCount() const
Definition: regchannel.cpp:474
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
EventReturn OnGroupCheckPriv(const AccessGroup *group, const Anope::string &priv) anope_override
Definition: cs_access.cpp:875
void AddAccess(ChanAccess *access)
Definition: regchannel.cpp:398
void DoAdd(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_access.cpp:89
CoreExport IRCDProto * IRCD
Definition: protocol.cpp:23
AccessProvider * provider
Definition: access.h:90
Anope::string name
Definition: access.h:34
EventReturn
Definition: modules.h:129
#define MODULE_INIT(x)
Definition: modules.h:45
CoreExport const char * Translate(const char *string)
Definition: language.cpp:59
ChanUserList users
Definition: channels.h:56
void DoDel(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_access.cpp:223
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
size_type find_first_not_of(const string &_str, size_type pos=0) const
Definition: anope.h:205
CommandCSAccess(Module *creator)
Definition: cs_access.cpp:491
AccessAccessProvider(Module *o)
Definition: cs_access.cpp:75
Anope::string stringify(const T &x)
Definition: anope.h:710
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: cs_access.cpp:557
virtual bool Matches(const User *u, const NickCore *nc, Path &p) const
Definition: access.cpp:254
#define CHAN_X_NOT_REGISTERED
Definition: language.h:84
Anope::string nick
Definition: users.h:62
AccessGroup AccessFor(ChannelInfo *ci)
Definition: command.cpp:41
virtual bool IsChannelValid(const Anope::string &)
Definition: protocol.cpp:372
static User * Find(const Anope::string &name, bool nick_only=false)
Definition: users.cpp:815
T anope_dynamic_static_cast(O ptr)
Definition: anope.h:774
int16_t GetLevel(const Anope::string &priv) const
Definition: regchannel.cpp:581
Anope::string name
Definition: access.cpp:22
Reference< NickCore > nc
Definition: commands.h:61
AccessAccessProvider accessprovider
Definition: cs_access.cpp:832
void SendSyntax(CommandSource &)
Definition: command.cpp:145
void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_access.cpp:613
NickCore * GetAccount()
Definition: command.cpp:36
void ClearLevels()
Definition: regchannel.cpp:605
void OnReload(Configuration::Conf *conf) anope_override
Definition: cs_access.cpp:844
const char * c_str() const
Definition: anope.h:117
#define CHAN_ACCESS_LEVEL_RANGE
Definition: language.h:108
Definition: logger.h:53
CSAccess(const Anope::string &modname, const Anope::string &creator)
Definition: cs_access.cpp:837
T Get(const Anope::string &tag)
Definition: config.h:44
Anope::string AccessSerialize() const
Definition: cs_access.cpp:37
static AccessAccessProvider * me
Definition: cs_access.cpp:73
void SetMask(const Anope::string &mask, ChannelInfo *ci)
Definition: access.cpp:165
CoreExport Anope::string strftime(time_t t, const NickCore *nc=NULL, bool short_output=false)
Definition: misc.cpp:356
void ClearAccess()
Definition: regchannel.cpp:513
void SetPermanent(bool state)
Definition: module.cpp:84
void DoReset(CommandSource &source, ChannelInfo *ci)
Definition: cs_access.cpp:719
#define _(x)
Definition: services.h:50
ListFormatter & AddColumn(const Anope::string &name)
Definition: misc.cpp:128
AccessChanAccess(AccessProvider *p)
Definition: cs_access.cpp:28
CommandCSLevels(Module *creator)
Definition: cs_access.cpp:732
ChanAccess * Create() anope_override
Definition: cs_access.cpp:80
void DoDisable(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_access.cpp:658
ChanAccess * GetAccess(unsigned index) const
Definition: regchannel.cpp:403
void DoView(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_access.cpp:458
bool HasPriv(const Anope::string &name) const anope_override
Definition: cs_access.cpp:32
bool operator>(const ChanAccess &other) const anope_override
Definition: cs_access.cpp:53
static void reset_levels(ChannelInfo *ci)
Definition: cs_access.cpp:16