Anope IRC Services  Version 2.0
cs_flags.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, char> defaultFlags;
15 
17 {
18  public:
19  std::set<char> flags;
20 
22  {
23  }
24 
25  bool HasPriv(const Anope::string &priv) const anope_override
26  {
27  std::map<Anope::string, char>::iterator it = defaultFlags.find(priv);
28  if (it != defaultFlags.end() && this->flags.count(it->second) > 0)
29  return true;
30  return false;
31  }
32 
34  {
35  return Anope::string(this->flags.begin(), this->flags.end());
36  }
37 
39  {
40  for (unsigned i = data.length(); i > 0; --i)
41  this->flags.insert(data[i - 1]);
42  }
43 
44  static Anope::string DetermineFlags(const ChanAccess *access)
45  {
46  if (access->provider->name == "access/flags")
47  return access->AccessSerialize();
48 
49  std::set<char> buffer;
50 
51  for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
52  if (access->HasPriv(it->first))
53  buffer.insert(it->second);
54 
55  if (buffer.empty())
56  return "(none)";
57  else
58  return Anope::string(buffer.begin(), buffer.end());
59  }
60 };
61 
63 {
64  public:
66 
67  FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags")
68  {
69  ap = this;
70  }
71 
73  {
74  return new FlagsChanAccess(this);
75  }
76 };
78 
79 class CommandCSFlags : public Command
80 {
81  void DoModify(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
82  {
83  Anope::string mask = params.size() > 2 ? params[2] : "";
84  Anope::string flags = params.size() > 3 ? params[3] : "";
85 
86  if (flags.empty())
87  {
88  this->OnSyntaxError(source, "");
89  return;
90  }
91 
92  AccessGroup u_access = source.AccessFor(ci);
93  const ChanAccess *highest = u_access.Highest();
94 
95  if (IRCD->IsChannelValid(mask))
96  {
97  if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access"))
98  {
99  source.Reply(_("Channels may not be on access lists."));
100  return;
101  }
102 
103  ChannelInfo *targ_ci = ChannelInfo::Find(mask);
104  if (targ_ci == NULL)
105  {
106  source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str());
107  return;
108  }
109  else if (ci == targ_ci)
110  {
111  source.Reply(_("You can't add a channel to its own access list."));
112  return;
113  }
114 
115  mask = targ_ci->name;
116  }
117  else
118  {
119  const NickAlias *na = NickAlias::Find(mask);
120  if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access"))
121  {
122  source.Reply(_("Masks and unregistered users may not be on access lists."));
123  return;
124  }
125  else if (mask.find_first_of("!*@") == Anope::string::npos && !na)
126  {
127  User *targ = User::Find(mask, true);
128  if (targ != NULL)
129  mask = "*!*@" + targ->GetDisplayedHost();
130  else
131  {
132  source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
133  return;
134  }
135  }
136  }
137 
138  ChanAccess *current = NULL;
139  unsigned current_idx;
140  std::set<char> current_flags;
141  bool override = false;
142  for (current_idx = ci->GetAccessCount(); current_idx > 0; --current_idx)
143  {
144  ChanAccess *access = ci->GetAccess(current_idx - 1);
145  if (mask.equals_ci(access->Mask()))
146  {
147  // Flags allows removing others that have the same access as you,
148  // but no other access system does.
149  if (highest && highest->provider != FlagsAccessProvider::ap && !u_access.founder)
150  // operator<= on the non-me entry!
151  if (*highest <= *access)
152  {
153  if (source.HasPriv("chanserv/access/modify"))
154  override = true;
155  else
156  {
157  source.Reply(ACCESS_DENIED);
158  return;
159  }
160  }
161 
162  current = access;
164  for (unsigned j = cur_flags.length(); j > 0; --j)
165  current_flags.insert(cur_flags[j - 1]);
166  break;
167  }
168  }
169 
170  unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024");
171  if (access_max && ci->GetDeepAccessCount() >= access_max)
172  {
173  source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max);
174  return;
175  }
176 
177  Privilege *p = NULL;
178  bool add = true;
179  for (size_t i = 0; i < flags.length(); ++i)
180  {
181  char f = flags[i];
182  switch (f)
183  {
184  case '+':
185  add = true;
186  break;
187  case '-':
188  add = false;
189  break;
190  case '*':
191  for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
192  {
193  bool has = current_flags.count(it->second);
194  // If we are adding a flag they already have or removing one they don't have, don't bother
195  if (add == has)
196  continue;
197 
198  if (!u_access.HasPriv(it->first) && !u_access.founder)
199  {
200  if (source.HasPriv("chanserv/access/modify"))
201  override = true;
202  else
203  continue;
204  }
205 
206  if (add)
207  current_flags.insert(it->second);
208  else
209  current_flags.erase(it->second);
210  }
211  break;
212  default:
214  if (p != NULL && defaultFlags[p->name])
215  {
216  f = defaultFlags[p->name];
217  i = flags.length();
218  }
219 
220  for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
221  {
222  if (f != it->second)
223  continue;
224  else if (!u_access.HasPriv(it->first) && !u_access.founder)
225  {
226  if (source.HasPriv("chanserv/access/modify"))
227  override = true;
228  else
229  {
230  source.Reply(_("You can not set the \002%c\002 flag."), f);
231  break;
232  }
233  }
234  if (add)
235  current_flags.insert(f);
236  else
237  current_flags.erase(f);
238  break;
239  }
240  }
241  }
242  if (current_flags.empty())
243  {
244  if (current != NULL)
245  {
246  ci->EraseAccess(current_idx - 1);
247  FOREACH_MOD(OnAccessDel, (ci, source, current));
248  delete current;
249  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
250  source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str());
251  }
252  else
253  {
254  source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str());
255  }
256  return;
257  }
258 
259  ServiceReference<AccessProvider> provider("AccessProvider", "access/flags");
260  if (!provider)
261  return;
263  access->SetMask(mask, ci);
264  access->creator = source.GetNick();
265  access->last_seen = current ? current->last_seen : 0;
266  access->created = Anope::CurTime;
267  access->flags = current_flags;
268 
269  if (current != NULL)
270  delete current;
271 
272  ci->AddAccess(access);
273 
274  FOREACH_MOD(OnAccessAdd, (ci, source, access));
275 
276  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize();
277  if (p != NULL)
278  {
279  if (add)
280  source.Reply(_("Privilege \002%s\002 added to \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
281  else
282  source.Reply(_("Privilege \002%s\002 removed from \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
283  }
284  else
285  source.Reply(_("Flags for \002%s\002 on %s set to +\002%s\002"), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
286  }
287 
288  void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
289  {
290  const Anope::string &arg = params.size() > 2 ? params[2] : "";
291 
292  if (!ci->GetAccessCount())
293  {
294  source.Reply(_("%s access list is empty."), ci->name.c_str());
295  return;
296  }
297 
298  ListFormatter list(source.GetAccount());
299 
300  list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Flags")).AddColumn(_("Creator")).AddColumn(_("Created"));
301 
302  unsigned count = 0;
303  for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
304  {
305  const ChanAccess *access = ci->GetAccess(i);
306  const Anope::string &flags = FlagsChanAccess::DetermineFlags(access);
307 
308  if (!arg.empty())
309  {
310  if (arg[0] == '+')
311  {
312  bool pass = true;
313  for (size_t j = 1; j < arg.length(); ++j)
314  if (flags.find(arg[j]) == Anope::string::npos)
315  pass = false;
316  if (pass == false)
317  continue;
318  }
319  else if (!Anope::Match(access->Mask(), arg))
320  continue;
321  }
322 
324  ++count;
325  entry["Number"] = stringify(i + 1);
326  entry["Mask"] = access->Mask();
327  entry["Flags"] = flags;
328  entry["Creator"] = access->creator;
329  entry["Created"] = Anope::strftime(access->created, source.nc, true);
330  list.AddEntry(entry);
331  }
332 
333  if (list.IsEmpty())
334  source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
335  else
336  {
337  std::vector<Anope::string> replies;
338  list.Process(replies);
339 
340  source.Reply(_("Flags list for %s"), ci->name.c_str());
341  for (unsigned i = 0; i < replies.size(); ++i)
342  source.Reply(replies[i]);
343  if (count == ci->GetAccessCount())
344  source.Reply(_("End of access list."));
345  else
346  source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount());
347  }
348  }
349 
351  {
352  if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
353  source.Reply(ACCESS_DENIED);
354  else
355  {
356  ci->ClearAccess();
357 
358  FOREACH_MOD(OnAccessClear, (ci, source));
359 
360  source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str());
361 
362  bool override = !source.IsFounder(ci);
363  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
364  }
365 
366  return;
367  }
368 
369  public:
370  CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 1, 4)
371  {
372  this->SetDesc(_("Modify the list of privileged users"));
373  this->SetSyntax(_("\037channel\037 MODIFY \037mask\037 \037changes\037"));
374  this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]"));
375  this->SetSyntax(_("\037channel\037 CLEAR"));
376  }
377 
378  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
379  {
380  const Anope::string &chan = params[0];
381  const Anope::string &cmd = params.size() > 1 ? params[1] : "";
382 
384  if (ci == NULL)
385  {
386  source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
387  return;
388  }
389 
390  bool is_list = cmd.empty() || cmd.equals_ci("LIST");
391  bool has_access = false;
392  if (source.HasPriv("chanserv/access/modify"))
393  has_access = true;
394  else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
395  has_access = true;
396  else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
397  has_access = true;
398 
399  if (!has_access)
400  source.Reply(ACCESS_DENIED);
401  else if (Anope::ReadOnly && !is_list)
402  source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
403  else if (cmd.equals_ci("MODIFY"))
404  this->DoModify(source, ci, params);
405  else if (is_list)
406  this->DoList(source, ci, params);
407  else if (cmd.equals_ci("CLEAR"))
408  this->DoClear(source, ci);
409  else
410  this->OnSyntaxError(source, cmd);
411  }
412 
413  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
414  {
415  this->SendSyntax(source);
416  source.Reply(" ");
417  source.Reply(_("%s is another way to modify the channel access list, similar to\n"
418  "the XOP and ACCESS methods."), source.command.c_str());
419  source.Reply(" ");
420  source.Reply(_("The \002MODIFY\002 command allows you to modify the access list. If mask is\n"
421  "not already on the access list is it added, then the changes are applied.\n"
422  "If the mask has no more flags, then the mask is removed from the access list.\n"
423  "Additionally, you may use +* or -* to add or remove all flags, respectively. You are\n"
424  "only able to modify the access list if you have the proper permission on the channel,\n"
425  "and even then you can only give other people access to up what you already have."));
426  source.Reply(" ");
427  source.Reply(_("The \002LIST\002 command allows you to list existing entries on the channel access list.\n"
428  "If a mask is given, the mask is wildcard matched against all existing entries on the\n"
429  "access list, and only those entries are returned. If a set of flags is given, only those\n"
430  "on the access list with the specified flags are returned."));
431  source.Reply(" ");
432  source.Reply(_("The \002CLEAR\002 command clears the channel access list, which requires channel founder."));
433  source.Reply(" ");
434  source.Reply(_("The available flags are:"));
435 
436  typedef std::multimap<char, Anope::string, ci::less> reverse_map;
437  reverse_map reverse;
438  for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
439  reverse.insert(std::make_pair(it->second, it->first));
440 
441  for (reverse_map::iterator it = reverse.begin(), it_end = reverse.end(); it != it_end; ++it)
442  {
444  if (p == NULL)
445  continue;
446  source.Reply(" %c - %s", it->first, Language::Translate(source.nc, p->desc.c_str()));
447  }
448 
449  return true;
450  }
451 };
452 
453 class CSFlags : public Module
454 {
457 
458  public:
459  CSFlags(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
460  accessprovider(this), commandcsflags(this)
461  {
462  this->SetPermanent(true);
463 
464  }
465 
467  {
468  defaultFlags.clear();
469 
470  for (int i = 0; i < conf->CountBlock("privilege"); ++i)
471  {
472  Configuration::Block *priv = conf->GetBlock("privilege", i);
473 
474  const Anope::string &pname = priv->Get<const Anope::string>("name");
475 
477  if (p == NULL)
478  continue;
479 
480  const Anope::string &value = priv->Get<const Anope::string>("flag");
481  if (value.empty())
482  continue;
483 
484  defaultFlags[p->name] = value[0];
485  }
486  }
487 };
488 
CoreExport bool ReadOnly
Definition: main.cpp:28
std::set< char > flags
Definition: cs_flags.cpp:19
static NickAlias * Find(const Anope::string &nick)
Definition: nickalias.cpp:121
FlagsAccessProvider accessprovider
Definition: cs_flags.cpp:455
static FlagsAccessProvider * ap
Definition: cs_flags.cpp:65
FlagsChanAccess(AccessProvider *p)
Definition: cs_flags.cpp:21
Definition: hashcomp.h:84
Anope::string name
Definition: regchannel.h:63
#define ACCESS_DENIED
Definition: language.h:73
void AccessUnserialize(const Anope::string &data) anope_override
Definition: cs_flags.cpp:38
Definition: users.h:34
#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
unsigned GetDeepAccessCount() const
Definition: regchannel.cpp:479
time_t created
Definition: access.h:95
bool founder
Definition: access.h:150
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
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: cs_flags.cpp:378
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
#define FOREACH_MOD(ename, args)
Definition: modules.h:62
iterator erase(const iterator &i)
Definition: anope.h:155
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: cs_flags.cpp:413
static Privilege * FindPrivilege(const Anope::string &name)
Definition: access.cpp:105
static ChannelInfo * Find(const Anope::string &name)
Definition: regchannel.cpp:630
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
size_type length() const
Definition: anope.h:131
bool HasPriv(const Anope::string &priv) const anope_override
Definition: cs_flags.cpp:25
FlagsAccessProvider(Module *o)
Definition: cs_flags.cpp:67
Block * GetBlock(const Anope::string &name, int num=0)
Definition: config.cpp:43
Definition: Config.cs:26
virtual bool HasPriv(const Anope::string &name) const =0
ChanAccess * Create() anope_override
Definition: cs_flags.cpp:72
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
CommandCSFlags(Module *creator)
Definition: cs_flags.cpp:370
Anope::string AccessSerialize() const
Definition: cs_flags.cpp:33
ChanAccess * EraseAccess(unsigned index)
Definition: regchannel.cpp:503
static const size_type npos
Definition: anope.h:44
void Reply(const char *message,...)
Definition: command.cpp:96
void DoModify(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_flags.cpp:81
const Anope::string & GetDisplayedHost() const
Definition: users.cpp:203
void OnReload(Configuration::Conf *conf) anope_override
Definition: cs_flags.cpp:466
const Anope::string & Mask() const
Definition: access.cpp:196
bool HasPriv(const Anope::string &cmd)
Definition: command.cpp:69
const ChanAccess * Highest() const
Definition: access.cpp:416
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
void AddAccess(ChanAccess *access)
Definition: regchannel.cpp:398
void DoList(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_flags.cpp:288
static std::map< Anope::string, char > defaultFlags
Definition: cs_flags.cpp:14
CoreExport IRCDProto * IRCD
Definition: protocol.cpp:23
std::basic_string< char, ci_char_traits, std::allocator< char > > string
Definition: hashcomp.h:133
AccessProvider * provider
Definition: access.h:90
Anope::string name
Definition: access.h:34
#define MODULE_INIT(x)
Definition: modules.h:45
CoreExport const char * Translate(const char *string)
Definition: language.cpp:59
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
Anope::string stringify(const T &x)
Definition: anope.h:710
Anope::string name
Definition: service.h:88
#define CHAN_X_NOT_REGISTERED
Definition: language.h:84
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
void DoClear(CommandSource &source, ChannelInfo *ci)
Definition: cs_flags.cpp:350
T anope_dynamic_static_cast(O ptr)
Definition: anope.h:774
Reference< NickCore > nc
Definition: commands.h:61
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
T Get(const Anope::string &tag)
Definition: config.h:44
void SetMask(const Anope::string &mask, ChannelInfo *ci)
Definition: access.cpp:165
CommandCSFlags commandcsflags
Definition: cs_flags.cpp:456
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
static Anope::string DetermineFlags(const ChanAccess *access)
Definition: cs_flags.cpp:44
void SetPermanent(bool state)
Definition: module.cpp:84
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
#define _(x)
Definition: services.h:50
ListFormatter & AddColumn(const Anope::string &name)
Definition: misc.cpp:128
CSFlags(const Anope::string &modname, const Anope::string &creator)
Definition: cs_flags.cpp:459
ChanAccess * GetAccess(unsigned index) const
Definition: regchannel.cpp:403