/*
* InspIRCd -- Internet Relay Chat Daemon
*
+ * Copyright (C) 2017-2020 Sadie Powell <sadie@witchery.services>
+ * Copyright (C) 2013, 2017-2018 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- * Copyright (C) 2007-2008 Craig Edwards <craigedwards@brainbox.cc>
- * Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006-2007, 2010 Craig Edwards <brain@inspircd.org>
*
* 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
#include "inspircd.h"
+
class MessageDetailsImpl : public MessageDetails
{
public:
{
// If the source is local and was not sending a CTCP reply then update their idle time.
LocalUser* lsource = IS_LOCAL(source);
- if (lsource && (msgdetails.type != MSG_NOTICE || !msgdetails.IsCTCP()))
+ if (lsource && msgdetails.update_idle && (msgdetails.type != MSG_NOTICE || !msgdetails.IsCTCP()))
lsource->idle_lastmsg = ServerInstance->Time();
// Inform modules that a message was sent.
{
private:
const MessageType msgtype;
- ChanModeReference moderatedmode;
- ChanModeReference noextmsgmode;
CmdResult HandleChannelTarget(User* source, const Params& parameters, const char* target, PrefixMode* pm)
{
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)");
- 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);
MessageDetailsImpl msgdetails(msgtype, parameters[1], parameters.GetTags());
// 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);
return CMD_FAILURE;
}
- // If the target is away then inform the user.
- if (target->IsAway() && msgtype == MSG_PRIVMSG)
- source->WriteNumeric(RPL_AWAY, target->nick, target->awaymsg);
-
// Fire the pre-message events.
MessageTarget msgtarget(target);
MessageDetailsImpl msgdetails(msgtype, parameters[1], parameters.GetTags());
if (!FirePreEvents(source, msgtarget, msgdetails))
return CMD_FAILURE;
+ // If the target is away then inform the user.
+ if (target->IsAway() && msgdetails.type == MSG_PRIVMSG)
+ source->WriteNumeric(RPL_AWAY, target->nick, target->awaymsg);
+
LocalUser* const localtarget = IS_LOCAL(target);
if (localtarget)
{
// Send to the target if they are a local user.
- ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, source, localtarget->nick, msgdetails.text, msgtype);
+ ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, source, localtarget->nick, msgdetails.text, msgdetails.type);
privmsg.AddTags(msgdetails.tags_out);
privmsg.SetSideEffect(true);
localtarget->Send(ServerInstance->GetRFCEvents().privmsg, privmsg);
CommandMessage(Module* parent, MessageType mt)
: Command(parent, ClientProtocol::Messages::Privmsg::CommandStrFromMsgType(mt), 2, 2)
, msgtype(mt)
- , moderatedmode(parent, "moderated")
- , noextmsgmode(parent, "noextmsg")
{
- syntax = "<target>{,<target>} <message>";
+ syntax = "<target>[,<target>]+ :<message>";
}
/** Handle command.
}
};
+class CommandSQuery : public SplitCommand
+{
+ public:
+ CommandSQuery(Module* Creator)
+ : SplitCommand(Creator, "SQUERY", 2, 2)
+ {
+ syntax = "<service> :<message>";
+ }
+
+ CmdResult HandleLocal(LocalUser* user, const Params& parameters) CXX11_OVERRIDE
+ {
+ // The specified message was empty.
+ if (parameters[1].empty())
+ {
+ user->WriteNumeric(ERR_NOTEXTTOSEND, "No text to send");
+ return CMD_FAILURE;
+ }
+
+ // The target can be either a nick or a nick@server mask.
+ User* target;
+ const char* targetserver = strchr(parameters[0].c_str(), '@');
+ if (targetserver)
+ {
+ // The target is a user on a specific server (e.g. jto@tolsun.oulu.fi).
+ target = ServerInstance->FindNickOnly(parameters[0].substr(0, targetserver - parameters[0].c_str()));
+ if (target && strcasecmp(target->server->GetName().c_str(), targetserver + 1))
+ target = NULL;
+ }
+ else
+ {
+ // The targer can be on any server.
+ target = ServerInstance->FindNickOnly(parameters[0]);
+ }
+
+ if (!target || target->registered != REG_ALL || !target->server->IsULine())
+ {
+ // The target user does not exist, is not fully registered, or is not a service.
+ user->WriteNumeric(ERR_NOSUCHSERVICE, parameters[0], "No such service");
+ return CMD_FAILURE;
+ }
+
+ // Fire the pre-message events.
+ MessageTarget msgtarget(target);
+ MessageDetailsImpl msgdetails(MSG_PRIVMSG, parameters[1], parameters.GetTags());
+ if (!FirePreEvents(user, msgtarget, msgdetails))
+ return CMD_FAILURE;
+
+ // The SQUERY command targets a service on a U-lined server. This can never
+ // be on the server local to the source so we don't need to do any routing
+ // logic and can forward it as a PRIVMSG.
+
+ // Fire the post-message event.
+ return FirePostEvent(user, msgtarget, msgdetails);
+ }
+};
+
class ModuleCoreMessage : public Module
{
private:
CommandMessage cmdprivmsg;
CommandMessage cmdnotice;
+ CommandSQuery cmdsquery;
+ ChanModeReference moderatedmode;
+ ChanModeReference noextmsgmode;
public:
ModuleCoreMessage()
: cmdprivmsg(this, MSG_PRIVMSG)
, cmdnotice(this, MSG_NOTICE)
+ , cmdsquery(this)
+ , moderatedmode(this, "moderated")
+ , noextmsgmode(this, "noextmsg")
{
}
+ ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) CXX11_OVERRIDE
+ {
+ if (!IS_LOCAL(user) || target.type != MessageTarget::TYPE_CHANNEL)
+ return MOD_RES_PASSTHRU;
+
+ 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", *moderatedmode));
+ 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 MOD_RES_PASSTHRU;
+ }
+
Version GetVersion() CXX11_OVERRIDE
{
- return Version("PRIVMSG, NOTICE", VF_CORE|VF_VENDOR);
+ return Version("Provides the NOTICE, PRIVMSG, and SQUERY commands", VF_CORE|VF_VENDOR);
}
};