cs_flags.cpp

Go to the documentation of this file.
00001 /* ChanServ core functions
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
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> &params)
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> &params)
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> &params) 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)