/* +------------------------------------+
* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
* InspIRCd: (C) 2002-2009 InspIRCd Development Team
* See: http://wiki.inspircd.org/Credits
*
* This program is free but copyrighted software; see
* the file COPYING for details.
*
* ---------------------------------------------------
*/
#include "inspircd.h"
#include "protocol.h"
#include "opflags.h"
class OpFlagProviderImpl : public OpFlagProvider
{
public:
LocalStringExt ext;
OpFlagProviderImpl(Module* parent) : OpFlagProvider(parent, "opflags"), ext(EXTENSIBLE_MEMBERSHIP, "flaglist", parent)
{
}
std::string GetFlags(Membership* memb)
{
std::string* v = ext.get(memb);
if (v)
return *v;
return "";
}
void SetFlags(Membership* memb, const std::string& flags, bool sendGlobal)
{
if (flags.empty())
ext.unset(memb);
else
ext.set(memb, flags);
if (sendGlobal)
{
parameterlist encap;
encap.push_back("*");
encap.push_back("OPFLAGS");
encap.push_back(memb->chan->name);
encap.push_back(memb->user->uuid);
encap.push_back(":=" + flags);
ServerInstance->PI->SendEncapsulatedData(encap);
}
}
std::string SetFlags(Membership* memb, const std::set<std::string>& flags, bool sendGlobal)
{
std::string v;
for(std::set<std::string>::iterator i = flags.begin(); i != flags.end(); i++)
{
if (i != flags.begin())
v.push_back(',');
v.append(*i);
}
SetFlags(memb, v, sendGlobal);
return v;
}
bool PermissionCheck(Membership* memb, const std::string& needed)
{
if (!memb || needed.empty())
return false;
std::string* mine = ext.get(memb);
if (!mine)
return false;
irc::commasepstream flags(needed);
std::string flag;
while (flags.GetToken(flag))
{
irc::commasepstream myflags(*mine);
std::string myflag;
while (myflags.GetToken(myflag))
{
if (flag == myflag)
return true;
}
}
return false;
}
};
class FlagCmd : public Command
{
public:
OpFlagProviderImpl prov;
unsigned int conflevel;
unsigned int usermax;
FlagCmd(Module* parent) : Command(parent, "OPFLAGS", 1), prov(parent)
{
syntax = "<channel> [nick] {+-=}[<flags>]";
TRANSLATE4(TR_TEXT, TR_NICK, TR_TEXT, TR_END);
}
CmdResult Handle(const std::vector<std::string> ¶meters, User *src)
{
Channel* chan = ServerInstance->FindChan(parameters[0]);
if (!chan)
{
src->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",
src->nick.c_str(), parameters[0].c_str());
return CMD_FAILURE;
}
if (parameters.size() == 1)
{
std::map<std::string, std::vector<std::string> > opflags;
std::string tmp;
for(UserMembCIter i = chan->GetUsers()->begin(); i != chan->GetUsers()->end(); ++i)
{
irc::commasepstream flags(prov.GetFlags(i->second));
while(flags.GetToken(tmp))
opflags[tmp].push_back(i->first->nick);
}
for(std::map<std::string, std::vector<std::string> >::iterator i = opflags.begin(); i != opflags.end(); ++i)
{
for(std::vector<std::string>::iterator j = i->second.begin(); j != i->second.end(); ++j)
{
if(j == i->second.begin())
tmp.clear();
else
tmp.push_back(',');
tmp.append(*j);
}
src->WriteNumeric(926, "%s %s %s %s :Opflag is held by these users", src->nick.c_str(),
chan->name.c_str(), i->first.c_str(), tmp.c_str());
}
src->WriteNumeric(925, "%s %s :End of channel opflags", src->nick.c_str(), chan->name.c_str());
return CMD_SUCCESS;
}
User* user = ServerInstance->FindNick(parameters[1]);
if (!user)
{
src->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",
src->nick.c_str(), parameters[1].c_str());
return CMD_FAILURE;
}
Membership* memb = chan->GetUser(user);
if (!memb)
{
src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel", src->nick.c_str(), user->nick.c_str(), chan->name.c_str());
return CMD_FAILURE;
}
std::string* ptr = prov.ext.get(memb);
if (parameters.size() == 2)
{
if(ptr)
src->WriteNumeric(923, "%s %s %s %s :Opflags held", src->nick.c_str(),
chan->name.c_str(), user->nick.c_str(), ptr->c_str());
else
src->WriteNumeric(924, "%s %s %s :No opflags held", src->nick.c_str(),
chan->name.c_str(), user->nick.c_str());
return CMD_SUCCESS;
}
std::string delta = parameters[2];
if (IS_LOCAL(src))
{
OpFlagPermissionData perm(src, chan, user, delta);
FOR_EACH_MOD(OnPermissionCheck, (perm));
if (!perm.result.check(chan->GetAccessRank(src) >= conflevel))
{
src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You cannot change opflags on this channel.",
src->nick.c_str(), chan->name.c_str());
return CMD_FAILURE;
}
}
std::string flag;
std::set<std::string> flags;
if (ptr)
{
irc::commasepstream myflags(*ptr);
while (myflags.GetToken(flag))
flags.insert(flag);
}
bool adding = true;
irc::commasepstream deltaflags(delta);
while (deltaflags.GetToken(flag))
{
if (flag[0] == '=')
{
flags.clear();
flag = flag.substr(1);
}
else if (flag[0] == '+' || flag[0] == '-')
{
adding = (flag[0] == '+');
flag = flag.substr(1);
}
if (flag.empty())
continue;
if (adding)
flags.insert(flag);
else
flags.erase(flag);
}
if (IS_LOCAL(src) && flags.size() > usermax)
{
src->WriteNumeric(920, "%s %s %s :User would have too many opflags set",
src->nick.c_str(), chan->name.c_str(), user->nick.c_str());
return CMD_FAILURE;
}
if (flags.empty())
{
prov.ext.unset(memb);
if (!IS_SERVER(src))
for (UserMembIter i = chan->userlist.begin(); i != chan->userlist.end(); ++i)
if (IS_LOCAL(i->first))
i->first->WriteNumeric(922, "%s %s %s %s :Removed all opflags from",
i->first->nick.c_str(), chan->name.c_str(), src->nick.c_str(),
user->nick.c_str());
}
else
{
std::string v = prov.SetFlags(memb, flags, false);
if (!IS_SERVER(src))
for (UserMembIter i = chan->userlist.begin(); i != chan->userlist.end(); ++i)
if (IS_LOCAL(i->first))
i->first->WriteNumeric(921, "%s %s %s %s %s :Set opflags to",
i->first->nick.c_str(), chan->name.c_str(), src->nick.c_str(),
user->nick.c_str(), v.c_str());
}
return CMD_SUCCESS;
}
RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
{
if (parameters.size() == 2)
return ROUTE_LOCALONLY;
return ROUTE_OPT_BCAST;
}
};
class ModuleOpFlags : public Module
{
FlagCmd cmd;
public:
ModuleOpFlags() : cmd(this)
{
}
void init()
{
ServerInstance->Modules->AddService(cmd);
ServerInstance->Modules->AddService(cmd.prov);
ServerInstance->Modules->AddService(cmd.prov.ext);
Implementation eventlist[] = { I_OnSyncChannel, I_OnPermissionCheck };
ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
}
void ReadConfig(ConfigReadStatus&)
{
ConfigTag* tag = ServerInstance->Config->GetTag("opflags");
cmd.conflevel = tag->getInt("level", OP_VALUE);
cmd.usermax = tag->getInt("maxflags", 15);
}
void OnSyncChannel(Channel* channel, SyncTarget* target)
{
const UserMembList* users = channel->GetUsers();
for(UserMembCIter u = users->begin(); u != users->end(); u++)
{
std::string* ptr = cmd.prov.ext.get(u->second);
if (ptr)
{
parameterlist flags;
flags.push_back(channel->name);
flags.push_back(u->first->uuid);
flags.push_back(*ptr);
target->SendEncap("OPFLAGS", flags);
}
}
}
void OnPermissionCheck(PermissionData& perm)
{
if (perm.chan && perm.source && perm.result == MOD_RES_PASSTHRU)
{
Membership* memb = perm.chan->GetUser(perm.source);
if (cmd.prov.PermissionCheck(memb, perm.name))
perm.result = MOD_RES_ALLOW;
}
}
Version GetVersion()
{
return Version("Provides per-channel access flags", VF_OPTCOMMON | VF_VENDOR);
}
};
MODULE_INIT(ModuleOpFlags)