00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "module.h"
00015
00016 static std::map<Anope::string, char> defaultFlags;
00017
00018 class FlagsChanAccess : public ChanAccess
00019 {
00020 public:
00021 std::set<char> flags;
00022
00023 FlagsChanAccess(AccessProvider *p) : ChanAccess(p)
00024 {
00025 }
00026
00027 bool HasPriv(const Anope::string &priv) const anope_override
00028 {
00029 std::map<Anope::string, char>::iterator it = defaultFlags.find(priv);
00030 if (it != defaultFlags.end() && this->flags.count(it->second) > 0)
00031 return true;
00032 return false;
00033 }
00034
00035 Anope::string AccessSerialize() const
00036 {
00037 return Anope::string(this->flags.begin(), this->flags.end());
00038 }
00039
00040 void AccessUnserialize(const Anope::string &data) anope_override
00041 {
00042 for (unsigned i = data.length(); i > 0; --i)
00043 this->flags.insert(data[i - 1]);
00044 }
00045
00046 static Anope::string DetermineFlags(const ChanAccess *access)
00047 {
00048 if (access->provider->name == "access/flags")
00049 return access->AccessSerialize();
00050
00051 std::set<char> buffer;
00052
00053 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
00054 if (access->HasPriv(it->first))
00055 buffer.insert(it->second);
00056
00057 return Anope::string(buffer.begin(), buffer.end());
00058 }
00059 };
00060
00061 class FlagsAccessProvider : public AccessProvider
00062 {
00063 public:
00064 FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags")
00065 {
00066 }
00067
00068 ChanAccess *Create() anope_override
00069 {
00070 return new FlagsChanAccess(this);
00071 }
00072 };
00073
00074 class CommandCSFlags : public Command
00075 {
00076 void DoModify(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00077 {
00078 Anope::string mask = params.size() > 2 ? params[2] : "";
00079 Anope::string flags = params.size() > 3 ? params[3] : "";
00080
00081 if (flags.empty())
00082 {
00083 this->OnSyntaxError(source, "");
00084 return;
00085 }
00086
00087 AccessGroup u_access = source.AccessFor(ci);
00088
00089 if (mask.find_first_of("!*@") == Anope::string::npos && !NickAlias::Find(mask))
00090 {
00091 User *targ = User::Find(mask, true);
00092 if (targ != NULL)
00093 mask = "*!*@" + targ->GetDisplayedHost();
00094 else
00095 {
00096 source.Reply(NICK_X_NOT_REGISTERED, mask.c_str());
00097 return;
00098 }
00099 }
00100
00101 ChanAccess *current = NULL;
00102 std::set<char> current_flags;
00103 for (unsigned i = ci->GetAccessCount(); i > 0; --i)
00104 {
00105 ChanAccess *access = ci->GetAccess(i - 1);
00106 if (mask.equals_ci(access->mask))
00107 {
00108 current = access;
00109 Anope::string cur_flags = FlagsChanAccess::DetermineFlags(access);
00110 for (unsigned j = cur_flags.length(); j > 0; --j)
00111 current_flags.insert(cur_flags[j - 1]);
00112 break;
00113 }
00114 }
00115
00116 if (ci->GetAccessCount() >= Config->CSAccessMax)
00117 {
00118 source.Reply(_("Sorry, you can only have %d access entries on a channel."), Config->CSAccessMax);
00119 return;
00120 }
00121
00122 bool override = false;
00123 int add = -1;
00124 for (size_t i = 0; i < flags.length(); ++i)
00125 {
00126 char f = flags[i];
00127 switch (f)
00128 {
00129 case '+':
00130 add = 1;
00131 break;
00132 case '-':
00133 add = 0;
00134 break;
00135 case '*':
00136 if (add == -1)
00137 break;
00138 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
00139 {
00140 if (!u_access.HasPriv(it->first))
00141 {
00142 if (source.HasPriv("chanserv/access/modify"))
00143 override = true;
00144 else
00145 continue;
00146 }
00147 if (add == 1)
00148 current_flags.insert(it->second);
00149 else if (add == 0)
00150 current_flags.erase(it->second);
00151 }
00152 break;
00153 default:
00154 if (add == -1)
00155 break;
00156 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
00157 {
00158 if (f != it->second)
00159 continue;
00160 else if (!u_access.HasPriv(it->first))
00161 {
00162 if (source.HasPriv("chanserv/access/modify"))
00163 override = true;
00164 else
00165 {
00166 source.Reply(_("You can not set the \002%c\002 flag."), f);
00167 break;
00168 }
00169 }
00170 if (add == 1)
00171 current_flags.insert(f);
00172 else if (add == 0)
00173 current_flags.erase(f);
00174 break;
00175 }
00176 }
00177 }
00178 if (current_flags.empty())
00179 {
00180 if (current != NULL)
00181 {
00182 FOREACH_MOD(I_OnAccessDel, OnAccessDel(ci, source, current));
00183 delete current;
00184 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask;
00185 source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str());
00186 }
00187 else
00188 {
00189 source.Reply(_("Insufficient flags given."));
00190 }
00191 return;
00192 }
00193
00194 ServiceReference<AccessProvider> provider("AccessProvider", "access/flags");
00195 if (!provider)
00196 return;
00197 FlagsChanAccess *access = anope_dynamic_static_cast<FlagsChanAccess *>(provider->Create());
00198 access->ci = ci;
00199 access->mask = mask;
00200 access->creator = source.GetNick();
00201 access->last_seen = current ? current->last_seen : 0;
00202 access->created = Anope::CurTime;
00203 access->flags = current_flags;
00204
00205 if (current != NULL)
00206 delete current;
00207
00208 ci->AddAccess(access);
00209
00210 FOREACH_MOD(I_OnAccessAdd, OnAccessAdd(ci, source, access));
00211
00212 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize();
00213 source.Reply(_("Access for \002%s\002 on %s set to +\002%s\002"), access->mask.c_str(), ci->name.c_str(), access->AccessSerialize().c_str());
00214
00215 return;
00216 }
00217
00218 void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00219 {
00220 const Anope::string &arg = params.size() > 2 ? params[2] : "";
00221
00222 if (!ci->GetAccessCount())
00223 {
00224 source.Reply(_("%s access list is empty."), ci->name.c_str());
00225 return;
00226 }
00227
00228 ListFormatter list;
00229
00230 list.AddColumn("Number").AddColumn("Mask").AddColumn("Flags").AddColumn("Creator").AddColumn("Created");
00231
00232 unsigned count = 0;
00233 for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i)
00234 {
00235 const ChanAccess *access = ci->GetAccess(i);
00236
00237 if (!arg.empty())
00238 {
00239 const Anope::string &flags = FlagsChanAccess::DetermineFlags(access);
00240
00241 if (arg[0] == '+')
00242 {
00243 bool pass = true;
00244 for (size_t j = 1; j < arg.length(); ++j)
00245 if (flags.find(arg[j]) == Anope::string::npos)
00246 pass = false;
00247 if (pass == false)
00248 continue;
00249 }
00250 else if (!Anope::Match(access->mask, arg))
00251 continue;
00252 }
00253
00254 ListFormatter::ListEntry entry;
00255 ++count;
00256 entry["Number"] = stringify(i + 1);
00257 entry["Mask"] = access->mask;
00258 entry["Flags"] = FlagsChanAccess::DetermineFlags(access);
00259 entry["Creator"] = access->creator;
00260 entry["Created"] = Anope::strftime(access->created, source.nc, true);
00261 list.AddEntry(entry);
00262 }
00263
00264 if (list.IsEmpty())
00265 source.Reply(_("No matching entries on %s access list."), ci->name.c_str());
00266 else
00267 {
00268 std::vector<Anope::string> replies;
00269 list.Process(replies);
00270
00271 source.Reply(_("Flags list for %s"), ci->name.c_str());
00272 for (unsigned i = 0; i < replies.size(); ++i)
00273 source.Reply(replies[i]);
00274 if (count == ci->GetAccessCount())
00275 source.Reply(_("End of access list."));
00276 else
00277 source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount());
00278 }
00279 }
00280
00281 void DoClear(CommandSource &source, ChannelInfo *ci)
00282 {
00283 if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify"))
00284 source.Reply(ACCESS_DENIED);
00285 else
00286 {
00287 ci->ClearAccess();
00288
00289 FOREACH_MOD(I_OnAccessClear, OnAccessClear(ci, source));
00290
00291 source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str());
00292
00293 bool override = !source.IsFounder(ci);
00294 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list";
00295 }
00296
00297 return;
00298 }
00299
00300 public:
00301 CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 2, 4)
00302 {
00303 this->SetDesc(_("Modify the list of privileged users"));
00304 this->SetSyntax(_("\037channel\037 MODIFY \037mask\037 \037changes\037"));
00305 this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]"));
00306 this->SetSyntax(_("\037channel\037 CLEAR"));
00307 }
00308
00309 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00310 {
00311 const Anope::string &chan = params[0];
00312 const Anope::string &cmd = params[1];
00313
00314 ChannelInfo *ci = ChannelInfo::Find(chan);
00315 if (ci == NULL)
00316 {
00317 source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str());
00318 return;
00319 }
00320
00321 bool is_list = cmd.equals_ci("LIST");
00322 bool has_access = false;
00323 if (source.HasPriv("chanserv/access/modify"))
00324 has_access = true;
00325 else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST"))
00326 has_access = true;
00327 else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE"))
00328 has_access = true;
00329
00330 if (!has_access)
00331 source.Reply(ACCESS_DENIED);
00332 else if (Anope::ReadOnly && !is_list)
00333 source.Reply(_("Sorry, channel access list modification is temporarily disabled."));
00334 else if (cmd.equals_ci("MODIFY"))
00335 this->DoModify(source, ci, params);
00336 else if (cmd.equals_ci("LIST"))
00337 this->DoList(source, ci, params);
00338 else if (cmd.equals_ci("CLEAR"))
00339 this->DoClear(source, ci);
00340 else
00341 this->OnSyntaxError(source, cmd);
00342 }
00343
00344 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00345 {
00346 this->SendSyntax(source);
00347 source.Reply(" ");
00348 source.Reply(_("%s is another way to modify the channel access list, similar to\n"
00349 "the XOP and ACCESS methods."), source.command.c_str());
00350 source.Reply(" ");
00351 source.Reply(_("The \002MODIFY\002 command allows you to modify the access list. If mask is\n"
00352 "not already on the access list is it added, then the changes are applied.\n"
00353 "If the mask has no more flags, then the mask is removed from the access list.\n"
00354 "Additionally, you may use +* or -* to add or remove all flags, respectively. You are\n"
00355 "only able to modify the access list if you have the proper permission on the channel,\n"
00356 "and even then you can only give other people access to up what you already have."));
00357 source.Reply(" ");
00358 source.Reply(_("The \002LIST\002 command allows you to list existing entries on the channel access list.\n"
00359 "If a mask is given, the mask is wildcard matched against all existing entries on the\n"
00360 "access list, and only those entries are returned. If a set of flags is given, only those\n"
00361 "on the access list with the specified flags are returned."));
00362 source.Reply(" ");
00363 source.Reply(_("The \002CLEAR\002 command clears the channel access list, which requires channel founder."));
00364 source.Reply(" ");
00365 source.Reply(_("The available flags are:"));
00366
00367 typedef std::multimap<char, Anope::string, ci::less> reverse_map;
00368 reverse_map reverse;
00369 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it)
00370 reverse.insert(std::make_pair(it->second, it->first));
00371
00372 for (reverse_map::iterator it = reverse.begin(), it_end = reverse.end(); it != it_end; ++it)
00373 {
00374 Privilege *p = PrivilegeManager::FindPrivilege(it->second);
00375 if (p == NULL)
00376 continue;
00377 source.Reply(" %c - %s", it->first, Language::Translate(source.nc, p->desc.c_str()));
00378 }
00379
00380 return true;
00381 }
00382 };
00383
00384 class CSFlags : public Module
00385 {
00386 FlagsAccessProvider accessprovider;
00387 CommandCSFlags commandcsflags;
00388
00389 public:
00390 CSFlags(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00391 accessprovider(this), commandcsflags(this)
00392 {
00393 this->SetAuthor("Anope");
00394 this->SetPermanent(true);
00395
00396 Implementation i[] = { I_OnReload };
00397 ModuleManager::Attach(i, this, 1);
00398
00399 this->OnReload();
00400 }
00401
00402 void OnReload() anope_override
00403 {
00404 ConfigReader config;
00405 defaultFlags.clear();
00406
00407 for (int i = 0; i < config.Enumerate("privilege"); ++i)
00408 {
00409 const Anope::string &pname = config.ReadValue("privilege", "name", "", i);
00410
00411 Privilege *p = PrivilegeManager::FindPrivilege(pname);
00412 if (p == NULL)
00413 continue;
00414
00415 const Anope::string &value = config.ReadValue("privilege", "flag", "", i);
00416 if (value.empty())
00417 continue;
00418
00419 defaultFlags[p->name] = value[0];
00420 }
00421 }
00422 };
00423
00424 MODULE_INIT(CSFlags)