/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2019 Peter Powell <petpow@saberuk.com>
- * Copyright (C) 2016 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
+ * Copyright (C) 2018-2021 Sadie Powell <sadie@witchery.services>
*
* This file is part of InspIRCd. InspIRCd is free software: you can
* redistribute it and/or modify it under the terms of the GNU General Public
{
private:
Cap::Capability& cap;
- ChanModeReference moderatedmode;
- ChanModeReference noextmsgmode;
Events::ModuleEventProvider tagevprov;
ClientProtocol::EventProvider msgevprov;
return false;
}
+ // Check whether a module zapped the message tags.
+ if (msgdetails.tags_out.empty())
+ {
+ source->WriteNumeric(ERR_NOTEXTTOSEND, "No tags to send");
+ return false;
+ }
+
// Inform modules that a TAGMSG is about to be sent.
FOREACH_MOD_CUSTOM(tagevprov, CTCTags::EventListener, OnUserTagMessage, (source, msgtarget, msgdetails));
return true;
{
// If the source is local then update its idle time.
LocalUser* lsource = IS_LOCAL(source);
- if (lsource)
+ if (lsource && msgdetails.update_idle)
lsource->idle_lastmsg = ServerInstance->Time();
// Inform modules that a TAGMSG was sent.
return CMD_FAILURE;
}
- if (IS_LOCAL(source))
- {
- if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(source))
- {
- // The noextmsg mode is set and the source is not in the channel.
- source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)");
- return CMD_FAILURE;
- }
-
- bool no_chan_priv = chan->GetPrefixValue(source) < VOICE_VALUE;
- if (no_chan_priv && chan->IsModeSet(moderatedmode))
- {
- // The moderated mode is set and the source has no status rank.
- source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m is set)");
- return CMD_FAILURE;
- }
-
- if (no_chan_priv && ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL && chan->IsBanned(source))
- {
- // The source is banned in the channel and restrictbannedusers is enabled.
- if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
- source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)");
- return CMD_FAILURE;
- }
- }
-
// Fire the pre-message events.
MessageTarget msgtarget(chan, pm ? pm->GetPrefix() : 0);
CTCTags::TagMessageDetails msgdetails(parameters.GetTags());
return CMD_FAILURE;
unsigned int minrank = pm ? pm->GetPrefixRank() : 0;
- CTCTags::TagMessage message(source, chan, parameters.GetTags());
+ CTCTags::TagMessage message(source, chan, msgdetails.tags_out, msgtarget.status);
+ message.SetSideEffect(true);
const Channel::MemberMap& userlist = chan->GetUsers();
for (Channel::MemberMap::const_iterator iter = userlist.begin(); iter != userlist.end(); ++iter)
{
LocalUser* luser = IS_LOCAL(iter->first);
- // Don't send to remote users or the user who is the source.
+ // Don't send to remote users or the user who is the source.
if (!luser || luser == source)
continue;
// If the source isn't allowed to mass message users then reject
// the attempt to mass-message users.
if (!source->HasPrivPermission("users/mass-message"))
+ {
+ source->WriteNumeric(ERR_NOPRIVILEGES, "Permission Denied - You do not have the required operator privileges");
return CMD_FAILURE;
+ }
// Extract the server glob match from the target parameter.
std::string servername(parameters[0], 1);
// the message out to the local users.
if (InspIRCd::Match(ServerInstance->Config->ServerName, servername))
{
- CTCTags::TagMessage message(source, "$*", parameters.GetTags());
+ CTCTags::TagMessage message(source, "$*", msgdetails.tags_out);
+ message.SetSideEffect(true);
const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
for (UserManager::LocalList::const_iterator iter = list.begin(); iter != list.end(); ++iter)
{
if (localtarget && cap.get(localtarget))
{
// Send to the target if they have the capability and are a local user.
- CTCTags::TagMessage message(source, localtarget, parameters.GetTags());
+ CTCTags::TagMessage message(source, localtarget, msgdetails.tags_out);
+ message.SetSideEffect(true);
localtarget->Send(msgevprov, message);
}
CommandTagMsg(Module* Creator, Cap::Capability& Cap)
: Command(Creator, "TAGMSG", 1)
, cap(Cap)
- , moderatedmode(Creator, "moderated")
- , noextmsgmode(Creator, "noextmsg")
, tagevprov(Creator, "event/tagmsg")
, msgevprov(Creator, "TAGMSG")
{
if (IS_LOCAL(user) && !cap.get(user))
return CMD_FAILURE;
+ // The specified message tags were empty.
+ if (parameters.GetTags().empty())
+ {
+ user->WriteNumeric(ERR_NOTEXTTOSEND, "No tags to send");
+ return CMD_FAILURE;
+ }
+
// The target is a server glob.
if (parameters[0][0] == '$')
return HandleServerTarget(user, parameters);
- // If the message begins with a status character then look it up.
+ // If the message begins with one or more status characters then look them up.
const char* target = parameters[0].c_str();
- PrefixMode* pmh = ServerInstance->Modes->FindPrefix(target[0]);
- if (pmh)
- target++;
+ PrefixMode* targetpfx = NULL;
+ for (PrefixMode* pfx; (pfx = ServerInstance->Modes->FindPrefix(target[0])); ++target)
+ {
+ // We want the lowest ranked prefix specified.
+ if (!targetpfx || pfx->GetPrefixRank() < targetpfx->GetPrefixRank())
+ targetpfx = pfx;
+ }
+
+ if (!target[0])
+ {
+ // The target consisted solely of prefix modes.
+ user->WriteNumeric(ERR_NORECIPIENT, "No recipient given");
+ return CMD_FAILURE;
+ }
// The target is a channel name.
if (*target == '#')
- return HandleChannelTarget(user, parameters, target, pmh);
+ return HandleChannelTarget(user, parameters, target, targetpfx);
// The target is a nickname.
return HandleUserTarget(user, parameters);
RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
{
- return ROUTE_MESSAGE(parameters[0]);
+ if (IS_LOCAL(user))
+ // This is handled by the OnUserPostTagMessage hook to split the LoopCall pieces
+ return ROUTE_LOCALONLY;
+ else
+ return ROUTE_MESSAGE(parameters[0]);
}
};
Cap::Capability& cap;
public:
+ bool allowclientonlytags;
C2CTags(Module* Creator, Cap::Capability& Cap)
: ClientProtocol::MessageTagProvider(Creator)
, cap(Cap)
{
// A client-only tag is prefixed with a plus sign (+) and otherwise conforms
// to the format specified in IRCv3.2 tags.
- if (tagname[0] != '+' || tagname.length() < 2)
+ if (tagname[0] != '+' || tagname.length() < 2 || !allowclientonlytags)
return MOD_RES_PASSTHRU;
// If the user is local then we check whether they have the message-tags cap
Cap::Capability cap;
CommandTagMsg cmd;
C2CTags c2ctags;
+ ChanModeReference moderatedmode;
+ ChanModeReference noextmsgmode;
ModResult CopyClientTags(const ClientProtocol::TagMap& tags_in, ClientProtocol::TagMap& tags_out)
{
, cap(this, "message-tags")
, cmd(this, cap)
, c2ctags(this, cap)
+ , moderatedmode(this, "moderated")
+ , noextmsgmode(this, "noextmsg")
+ {
+ }
+
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ c2ctags.allowclientonlytags = ServerInstance->Config->ConfValue("ctctags")->getBool("allowclientonlytags", true);
+ }
+
+ void On005Numeric(std::map<std::string, std::string>& tokens) CXX11_OVERRIDE
{
+ if (!c2ctags.allowclientonlytags)
+ tokens["CLIENTTAGDENY"] = "*";
}
ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
ModResult OnUserPreTagMessage(User* user, const MessageTarget& target, CTCTags::TagMessageDetails& details) CXX11_OVERRIDE
{
+ if (IS_LOCAL(user) && target.type == MessageTarget::TYPE_CHANNEL)
+ {
+ Channel* chan = target.Get<Channel>();
+ if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(user))
+ {
+ // The noextmsg mode is set and the user is not in the channel.
+ user->WriteNumeric(Numerics::CannotSendTo(chan, "external messages", *noextmsgmode));
+ return MOD_RES_DENY;
+ }
+
+ bool no_chan_priv = chan->GetPrefixValue(user) < VOICE_VALUE;
+ if (no_chan_priv && chan->IsModeSet(moderatedmode))
+ {
+ // The moderated mode is set and the user has no status rank.
+ user->WriteNumeric(Numerics::CannotSendTo(chan, "messages", *noextmsgmode));
+ return MOD_RES_DENY;
+ }
+
+ if (no_chan_priv && ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL && chan->IsBanned(user))
+ {
+ // The user is banned in the channel and restrictbannedusers is enabled.
+ if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY)
+ user->WriteNumeric(Numerics::CannotSendTo(chan, "You cannot send messages to this channel whilst banned."));
+ return MOD_RES_DENY;
+ }
+ }
+
return CopyClientTags(details.tags_in, details.tags_out);
}
Version GetVersion() CXX11_OVERRIDE
{
- return Version("Provides the DRAFT message-tags IRCv3 extension", VF_VENDOR | VF_COMMON);
+ return Version("Provides the IRCv3 message-tags client capability.", VF_VENDOR | VF_COMMON);
}
};