diff options
author | Adam <Adam@anope.org> | 2013-07-03 22:45:00 -0400 |
---|---|---|
committer | Adam <Adam@anope.org> | 2013-07-03 22:45:00 -0400 |
commit | 7f971043bc0b552e0ec2f3a88bf123cecb007387 (patch) | |
tree | 2cc22c4b2c2d0231567624ad5d7595fbb97662ed | |
parent | c2e1a8a3e2bc2144ad0c4c6009be3ea974aef58f (diff) |
Allow channels on access lists
-rw-r--r-- | data/chanserv.example.conf | 10 | ||||
-rw-r--r-- | include/access.h | 14 | ||||
-rw-r--r-- | include/modules.h | 6 | ||||
-rw-r--r-- | include/regchannel.h | 9 | ||||
-rw-r--r-- | modules/commands/cs_access.cpp | 72 | ||||
-rw-r--r-- | modules/commands/cs_flags.cpp | 53 | ||||
-rw-r--r-- | modules/commands/cs_xop.cpp | 58 | ||||
-rw-r--r-- | modules/cs_statusupdate.cpp | 14 | ||||
-rw-r--r-- | modules/pseudoclients/chanserv.cpp | 2 | ||||
-rw-r--r-- | src/access.cpp | 86 | ||||
-rw-r--r-- | src/regchannel.cpp | 56 |
11 files changed, 294 insertions, 86 deletions
diff --git a/data/chanserv.example.conf b/data/chanserv.example.conf index 184c170fd..fb7daf26a 100644 --- a/data/chanserv.example.conf +++ b/data/chanserv.example.conf @@ -153,6 +153,16 @@ module * and chanserv/ban. */ reasonmax = 200 + + /* + * If set, prevents channel access entries from containing hostmasks. + */ + disallow_hostmask_access = false + + /* + * If set, prevents channels from being on access lists. + */ + disallow_channel_access = false } /* diff --git a/include/access.h b/include/access.h index f60625ad9..19b6b3b02 100644 --- a/include/access.h +++ b/include/access.h @@ -75,6 +75,13 @@ class CoreExport AccessProvider : public Service class CoreExport ChanAccess : public Serializable { public: + typedef std::multimap<const ChanAccess *, const ChanAccess *> Set; + /* shows the 'path' taken to determine if an access entry matches a user + * .first are access entries checked + * .second are access entries which match + */ + typedef std::pair<Set, Set> Path; + /* The provider that created this access entry */ AccessProvider *provider; /* Channel this access entry is on */ @@ -95,8 +102,9 @@ class CoreExport ChanAccess : public Serializable /** Check if this access entry matches the given user or account * @param u The user * @param nc The account + * @param p The path to the access object which matches will be put here */ - virtual bool Matches(const User *u, const NickCore *nc) const; + virtual bool Matches(const User *u, const NickCore *nc, Path &p) const; /** Check if this access entry has the given privilege. * @param name The privilege name @@ -127,8 +135,10 @@ class CoreExport ChanAccess : public Serializable class CoreExport AccessGroup : public std::vector<ChanAccess *> { public: - /* Channel these access entries are on */ + /* Channel these access entries are on */ const ChannelInfo *ci; + /* Path from these entries to other entries that they depend on */ + ChanAccess::Path path; /* Account these entries affect, if any */ const NickCore *nc; /* super_admin always gets all privs. founder is a special case where ci->founder == nc */ diff --git a/include/modules.h b/include/modules.h index ffb8bbcc7..8948d3389 100644 --- a/include/modules.h +++ b/include/modules.h @@ -597,10 +597,10 @@ class CoreExport Module : public Extensible */ virtual void OnBotDelete(BotInfo *bi) { throw NotImplementedException(); } - /** Called when access is deleted from a channel + /** Called after an access entry is deleted from a channel * @param ci The channel * @param source The source of the command - * @param access The access entry being removed + * @param access The access entry that was removed */ virtual void OnAccessDel(ChannelInfo *ci, CommandSource &source, ChanAccess *access) { throw NotImplementedException(); } @@ -704,7 +704,7 @@ class CoreExport Module : public Extensible * @param priv The privilege being checked for * @return EVENT_ALLOW for yes, EVENT_STOP to stop all processing */ - virtual EventReturn OnCheckPriv(ChanAccess *access, const Anope::string &priv) { throw NotImplementedException(); } + virtual EventReturn OnCheckPriv(const ChanAccess *access, const Anope::string &priv) { throw NotImplementedException(); } /** Check whether an access group has a privilege * @param group The group diff --git a/include/regchannel.h b/include/regchannel.h index f04156d89..49fc882e2 100644 --- a/include/regchannel.h +++ b/include/regchannel.h @@ -137,13 +137,18 @@ class CoreExport ChannelInfo : public Serializable, public Extensible */ unsigned GetAccessCount() const; + /** Get the number of access entries for this channel, + * including those that are on other channels. + */ + unsigned GetDeepAccessCount() const; + /** Erase an entry from the channel access list * * @param index The index in the access list vector * - * Clears the memory used by the given access entry and removes it from the vector. + * @return The erased entry */ - void EraseAccess(unsigned index); + ChanAccess *EraseAccess(unsigned index); /** Clear the entire channel access list * diff --git a/modules/commands/cs_access.cpp b/modules/commands/cs_access.cpp index 8562efeb4..9a90b7d5f 100644 --- a/modules/commands/cs_access.cpp +++ b/modules/commands/cs_access.cpp @@ -128,16 +128,47 @@ class CommandCSAccess : public Command } } - if (mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask)) + if (IRCD->IsChannelValid(mask)) { - User *targ = User::Find(mask, true); - if (targ != NULL) - mask = "*!*@" + targ->GetDisplayedHost(); - else + if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access")) { - source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); + source.Reply(_("Channels may not be on access lists.")); + return; + } + + ChannelInfo *targ_ci = ChannelInfo::Find(mask); + if (targ_ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str()); + return; + } + else if (ci == targ_ci) + { + source.Reply(_("You can't add a channel to its own access list.")); return; } + + mask = targ_ci->name; + } + else + { + const NickAlias *na = NickAlias::Find(mask); + if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access")) + { + source.Reply(_("Masks and unregistered users may not be on access lists.")); + return; + } + else if (mask.find_first_of("!*@") == Anope::string::npos && !na) + { + User *targ = User::Find(mask, true); + if (targ != NULL) + mask = "*!*@" + targ->GetDisplayedHost(); + else + { + source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); + return; + } + } } for (unsigned i = ci->GetAccessCount(); i > 0; --i) @@ -151,15 +182,15 @@ class CommandCSAccess : public Command source.Reply(ACCESS_DENIED); return; } - ci->EraseAccess(i - 1); + delete ci->EraseAccess(i - 1); break; } } unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024"); - if (access_max && ci->GetAccessCount() >= access_max) + if (access_max && ci->GetDeepAccessCount() >= access_max) { - source.Reply(_("Sorry, you can only have %d access entries on a channel."), access_max); + source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max); return; } @@ -187,7 +218,7 @@ class CommandCSAccess : public Command { Anope::string mask = params[2]; - if (!isdigit(mask[0]) && mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask)) + if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickAlias::Find(mask)) { User *targ = User::Find(mask, true); if (targ != NULL) @@ -258,9 +289,10 @@ class CommandCSAccess : public Command else Nicks = access->mask; - FOREACH_MOD(OnAccessDel, (ci, source, access)); - ci->EraseAccess(Number - 1); + + FOREACH_MOD(OnAccessDel, (ci, source, access)); + delete access; } } delcallback(source, ci, this, mask); @@ -284,6 +316,7 @@ class CommandCSAccess : public Command bool override = !u_access.founder && !u_access.HasPriv("ACCESS_CHANGE") && !access->mask.equals_ci(source.nc->display); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << access->mask; + ci->EraseAccess(i - 1); FOREACH_MOD(OnAccessDel, (ci, source, access)); delete access; } @@ -325,8 +358,11 @@ class CommandCSAccess : public Command Anope::string timebuf; if (ci->c) for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) - if (access->Matches(cit->second->user, cit->second->user->Account())) + { + ChanAccess::Path p; + if (access->Matches(cit->second->user, cit->second->user->Account(), p)) timebuf = "Now"; + } if (timebuf.empty()) { if (access->last_seen == 0) @@ -359,8 +395,11 @@ class CommandCSAccess : public Command Anope::string timebuf; if (ci->c) for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) - if (access->Matches(cit->second->user, cit->second->user->Account())) + { + ChanAccess::Path p; + if (access->Matches(cit->second->user, cit->second->user->Account(), p)) timebuf = "Now"; + } if (timebuf.empty()) { if (access->last_seen == 0) @@ -527,10 +566,13 @@ class CommandCSAccess : public Command "the level specified in the command. The \037level\037 specified\n" "must be less than that of the user giving the command, and\n" "if the \037mask\037 is already on the access list, the current\n" - "access level of that nick must be less than the access level\n" + "access level of that mask must be less than the access level\n" "of the user giving the command. When a user joins the channel\n" "the access they receive is from the highest level entry in the\n" "access list.")); + if (!Config->GetModule("chanserv")->Get<bool>("disallow_channel_access")) + source.Reply(_("The given mask may also be a channel, which will use the\n" + "access list from the other channel up to the given \037level\037")); source.Reply(" "); source.Reply(_("The \002ACCESS DEL\002 command removes the given nick from the\n" "access list. If a list of entry numbers is given, those\n" diff --git a/modules/commands/cs_flags.cpp b/modules/commands/cs_flags.cpp index 23e6914d3..50c8e6b49 100644 --- a/modules/commands/cs_flags.cpp +++ b/modules/commands/cs_flags.cpp @@ -84,23 +84,55 @@ class CommandCSFlags : public Command AccessGroup u_access = source.AccessFor(ci); - if (mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask)) + if (IRCD->IsChannelValid(mask)) { - User *targ = User::Find(mask, true); - if (targ != NULL) - mask = "*!*@" + targ->GetDisplayedHost(); - else + if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access")) + { + source.Reply(_("Channels may not be on access lists.")); + return; + } + + ChannelInfo *targ_ci = ChannelInfo::Find(mask); + if (targ_ci == NULL) { - source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); + source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str()); return; } + else if (ci == targ_ci) + { + source.Reply(_("You can't add a channel to its own access list.")); + return; + } + + mask = targ_ci->name; + } + else + { + const NickAlias *na = NickAlias::Find(mask); + if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access")) + { + source.Reply(_("Masks and unregistered users may not be on access lists.")); + return; + } + else if (mask.find_first_of("!*@") == Anope::string::npos && !na) + { + User *targ = User::Find(mask, true); + if (targ != NULL) + mask = "*!*@" + targ->GetDisplayedHost(); + else + { + source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); + return; + } + } } ChanAccess *current = NULL; + unsigned current_idx; std::set<char> current_flags; - for (unsigned i = ci->GetAccessCount(); i > 0; --i) + for (current_idx = ci->GetAccessCount(); current_idx > 0; --current_idx) { - ChanAccess *access = ci->GetAccess(i - 1); + ChanAccess *access = ci->GetAccess(current_idx - 1); if (mask.equals_ci(access->mask)) { current = access; @@ -112,9 +144,9 @@ class CommandCSFlags : public Command } unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024"); - if (access_max && ci->GetAccessCount() >= access_max) + if (access_max && ci->GetDeepAccessCount() >= access_max) { - source.Reply(_("Sorry, you can only have %d access entries on a channel."), access_max); + source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max); return; } @@ -178,6 +210,7 @@ class CommandCSFlags : public Command { if (current != NULL) { + ci->EraseAccess(current_idx - 1); FOREACH_MOD(OnAccessDel, (ci, source, current)); delete current; Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask; diff --git a/modules/commands/cs_xop.cpp b/modules/commands/cs_xop.cpp index 253dde884..db4faa657 100644 --- a/modules/commands/cs_xop.cpp +++ b/modules/commands/cs_xop.cpp @@ -132,16 +132,47 @@ class CommandCSXOP : public Command } } - if (mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask)) + if (IRCD->IsChannelValid(mask)) { - User *targ = User::Find(mask, true); - if (targ != NULL) - mask = "*!*@" + targ->GetDisplayedHost(); - else + if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access")) { - source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); + source.Reply(_("Channels may not be on access lists.")); + return; + } + + ChannelInfo *targ_ci = ChannelInfo::Find(mask); + if (targ_ci == NULL) + { + source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str()); return; } + else if (ci == targ_ci) + { + source.Reply(_("You can't add a channel to its own access list.")); + return; + } + + mask = targ_ci->name; + } + else + { + const NickAlias *na = NickAlias::Find(mask); + if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access")) + { + source.Reply(_("Masks and unregistered users may not be on access lists.")); + return; + } + else if (mask.find_first_of("!*@") == Anope::string::npos && !na) + { + User *targ = User::Find(mask, true); + if (targ != NULL) + mask = "*!*@" + targ->GetDisplayedHost(); + else + { + source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); + return; + } + } } for (unsigned i = 0; i < ci->GetAccessCount(); ++i) @@ -156,15 +187,15 @@ class CommandCSXOP : public Command return; } - ci->EraseAccess(i); + delete ci->EraseAccess(i); break; } } unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024"); - if (access_max && ci->GetAccessCount() >= access_max) + if (access_max && ci->GetDeepAccessCount() >= access_max) { - source.Reply(_("Sorry, you can only have %d %s entries on a channel."), access_max, source.command.c_str()); + source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max); return; } @@ -217,7 +248,7 @@ class CommandCSXOP : public Command const ChanAccess *highest = access.Highest(); bool override = false; - if (!isdigit(mask[0]) && mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask)) + if (!isdigit(mask[0]) && mask.find_first_of("#!*@") == Anope::string::npos && !NickAlias::Find(mask)) { User *targ = User::Find(mask, true); if (targ != NULL) @@ -287,9 +318,9 @@ class CommandCSXOP : public Command else nicks = caccess->mask; - FOREACH_MOD(OnAccessDel, (ci, source, caccess)); - ci->EraseAccess(number - 1); + FOREACH_MOD(OnAccessDel, (ci, source, caccess)); + delete caccess; } } delcallback(source, ci, this, override, mask); @@ -307,6 +338,7 @@ class CommandCSXOP : public Command source.Reply(_("\002%s\002 deleted from %s %s list."), a->mask.c_str(), ci->name.c_str(), source.command.c_str()); + ci->EraseAccess(i); FOREACH_MOD(OnAccessDel, (ci, source, a)); delete a; @@ -428,7 +460,7 @@ class CommandCSXOP : public Command { const ChanAccess *access = ci->GetAccess(i - 1); if (XOPChanAccess::DetermineLevel(access) == source.command.upper()) - ci->EraseAccess(i - 1); + delete ci->EraseAccess(i - 1); } FOREACH_MOD(OnAccessClear, (ci, source)); diff --git a/modules/cs_statusupdate.cpp b/modules/cs_statusupdate.cpp index b4294fbd6..499bfbe8c 100644 --- a/modules/cs_statusupdate.cpp +++ b/modules/cs_statusupdate.cpp @@ -22,12 +22,15 @@ class StatusUpdate : public Module { User *user = it->second->user; - if (user->server != Me && access->Matches(user, user->Account())) + ChanAccess::Path p; + if (user->server != Me && access->Matches(user, user->Account(), p)) { + AccessGroup ag = ci->AccessFor(user); + for (unsigned i = 0; i < ModeManager::GetStatusChannelModesByRank().size(); ++i) { ChannelModeStatus *cms = ModeManager::GetStatusChannelModesByRank()[i]; - if (!access->HasPriv("AUTO" + cms->name)) + if (!ag.HasPriv("AUTO" + cms->name)) ci->c->RemoveMode(NULL, cms, user->GetUID()); } ci->c->SetCorrectModes(user, true); @@ -42,13 +45,10 @@ class StatusUpdate : public Module { User *user = it->second->user; - if (user->server != Me && access->Matches(user, user->Account())) + ChanAccess::Path p; + if (user->server != Me && access->Matches(user, user->Account(), p)) { - /* Get user's current access and remove the entry about to be deleted */ AccessGroup ag = ci->AccessFor(user); - AccessGroup::iterator iter = std::find(ag.begin(), ag.end(), access); - if (iter != ag.end()) - ag.erase(iter); for (unsigned i = 0; i < ModeManager::GetStatusChannelModesByRank().size(); ++i) { diff --git a/modules/pseudoclients/chanserv.cpp b/modules/pseudoclients/chanserv.cpp index 2f7147cc6..b7fb67daf 100644 --- a/modules/pseudoclients/chanserv.cpp +++ b/modules/pseudoclients/chanserv.cpp @@ -182,7 +182,7 @@ class ChanServCore : public Module, public ChanServService if (anc && anc == nc) { - ci->EraseAccess(j); + delete ci->EraseAccess(j); break; } } diff --git a/src/access.cpp b/src/access.cpp index b0c98b9db..173f3c979 100644 --- a/src/access.cpp +++ b/src/access.cpp @@ -15,6 +15,7 @@ #include "regchannel.h" #include "users.h" #include "account.h" +#include "protocol.h" static struct { @@ -172,7 +173,7 @@ Serializable* ChanAccess::Unserialize(Serializable *obj, Serialize::Data &data) Anope::string provider, chan; data["provider"] >> provider; - data["ci"] >>chan; + data["ci"] >> chan; ServiceReference<AccessProvider> aprovider("AccessProvider", provider); ChannelInfo *ci = ChannelInfo::Find(chan); @@ -199,20 +200,56 @@ Serializable* ChanAccess::Unserialize(Serializable *obj, Serialize::Data &data) return access; } -bool ChanAccess::Matches(const User *u, const NickCore *acc) const +bool ChanAccess::Matches(const User *u, const NickCore *acc, Path &p) const { - bool is_mask = this->mask.find_first_of("!@?*") != Anope::string::npos; - if (u && is_mask && Anope::Match(u->nick, this->mask)) - return true; - else if (u && Anope::Match(u->GetDisplayedMask(), this->mask)) - return true; - else if (acc) + if (this->nc) + return this->nc == acc; + + if (u) + { + bool is_mask = this->mask.find_first_of("!@?*") != Anope::string::npos; + if (is_mask && Anope::Match(u->nick, this->mask)) + return true; + else if (Anope::Match(u->GetDisplayedMask(), this->mask)) + return true; + } + + if (acc) + { for (unsigned i = 0; i < acc->aliases->size(); ++i) { const NickAlias *na = acc->aliases->at(i); if (Anope::Match(na->nick, this->mask)) return true; } + } + + if (IRCD->IsChannelValid(this->mask)) + { + ChannelInfo *tci = ChannelInfo::Find(this->mask); + if (tci) + { + for (unsigned i = 0; i < tci->GetAccessCount(); ++i) + { + ChanAccess *a = tci->GetAccess(i); + std::pair<const ChanAccess *, const ChanAccess *> pair = std::make_pair(this, a); + + std::pair<Set::iterator, Set::iterator> range = p.first.equal_range(this); + for (; range.first != range.second; ++range.first) + if (range.first->first == pair.first && range.first->second == pair.second) + goto cont; + + p.first.insert(pair); + if (a->Matches(u, acc, p)) + p.second.insert(pair); + + cont:; + } + + return p.second.count(this) > 0; + } + } + return false; } @@ -299,6 +336,32 @@ AccessGroup::AccessGroup() : std::vector<ChanAccess *>() this->super_admin = this->founder = false; } +static bool HasPriv(const AccessGroup &ag, const ChanAccess *access, const Anope::string &name) +{ + EventReturn MOD_RESULT; + FOREACH_RESULT(OnCheckPriv, MOD_RESULT, (access, name)); + if (MOD_RESULT == EVENT_ALLOW || access->HasPriv(name)) + { + typedef std::multimap<const ChanAccess *, const ChanAccess *> path; + std::pair<path::const_iterator, path::const_iterator> it = ag.path.second.equal_range(access); + if (it.first != it.second) + /* check all of the paths for this entry */ + for (; it.first != it.second; ++it.first) + { + const ChanAccess *a = it.first->second; + /* if only one path fully matches then we are ok */ + if (HasPriv(ag, a, name)) + return true; + } + else + /* entry is the end of a chain, all entries match, ok */ + return true; + } + + /* entry does not match or none of the chains fully match */ + return false; +} + bool AccessGroup::HasPriv(const Anope::string &name) const { if (this->super_admin) @@ -307,17 +370,20 @@ bool AccessGroup::HasPriv(const Anope::string &name) const return false; else if (this->founder) return true; + EventReturn MOD_RESULT; FOREACH_RESULT(OnGroupCheckPriv, MOD_RESULT, (this, name)); if (MOD_RESULT != EVENT_CONTINUE) return MOD_RESULT == EVENT_ALLOW; + for (unsigned i = this->size(); i > 0; --i) { ChanAccess *access = this->at(i - 1); - FOREACH_RESULT(OnCheckPriv, MOD_RESULT, (access, name)); - if (MOD_RESULT == EVENT_ALLOW || access->HasPriv(name)) + + if (::HasPriv(*this, access, name)) return true; } + return false; } diff --git a/src/regchannel.cpp b/src/regchannel.cpp index 83cf2407f..8cc08f15d 100644 --- a/src/regchannel.cpp +++ b/src/regchannel.cpp @@ -400,16 +400,8 @@ AccessGroup ChannelInfo::AccessFor(const User *u) for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i) { ChanAccess *a = this->GetAccess(i); - - if (a->nc) - { - if (a->nc == nc) - group.push_back(a); - } - else if (a->Matches(u, nc)) - { + if (a->Matches(u, u->Account(), group.path)) group.push_back(a); - } } if (group.founder || !group.empty()) @@ -434,18 +426,10 @@ AccessGroup ChannelInfo::AccessFor(const NickCore *nc) for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i) { ChanAccess *a = this->GetAccess(i); - - if (a->nc) - { - if (a->nc == nc) - group.push_back(a); - } - else if (this->GetAccess(i)->Matches(NULL, nc)) - { - group.push_back(this->GetAccess(i)); - } + if (a->Matches(NULL, nc, group.path)) + group.push_back(a); } - + if (group.founder || !group.empty()) { this->last_used = Anope::CurTime; @@ -462,12 +446,38 @@ unsigned ChannelInfo::GetAccessCount() const return this->access->size(); } -void ChannelInfo::EraseAccess(unsigned index) +unsigned ChannelInfo::GetDeepAccessCount() const +{ + ChanAccess::Path path; + for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i) + { + ChanAccess *a = this->GetAccess(i); + a->Matches(NULL, NULL, path); + } + + unsigned count = this->GetAccessCount(); + std::set<const ChannelInfo *> channels; + channels.insert(this); + for (ChanAccess::Set::iterator it = path.first.begin(); it != path.first.end(); ++it) + { + const ChannelInfo *ci = it->first->ci; + if (!channels.count(ci)) + { + channels.count(ci); + count += ci->GetAccessCount(); + } + } + return count; +} + +ChanAccess *ChannelInfo::EraseAccess(unsigned index) { if (this->access->empty() || index >= this->access->size()) - return; + return NULL; - delete this->access->at(index); + ChanAccess *ca = this->access->at(index); + this->access->erase(this->access->begin() + index); + return ca; } void ChannelInfo::ClearAccess() |