mirror of
https://github.com/inspircd/inspircd.git
synced 2025-03-10 02:59:01 -04:00
Migrate code from ModeParser into cmd_mode (core_user)
- Process() that takes a std::vector<std::string> - DisplayCurrentModes() - DisplayListModes()
This commit is contained in:
parent
a1b74f4dfb
commit
3eda212c2a
@ -530,15 +530,6 @@ class CoreExport ModeParser : public fakederef<ModeParser>
|
|||||||
*/
|
*/
|
||||||
Modes::ChangeList LastChangeList;
|
Modes::ChangeList LastChangeList;
|
||||||
|
|
||||||
/** Displays the current modes of a channel or user.
|
|
||||||
* Used by ModeParser::Process.
|
|
||||||
*/
|
|
||||||
void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel);
|
|
||||||
/** Displays the value of a list mode
|
|
||||||
* Used by ModeParser::Process.
|
|
||||||
*/
|
|
||||||
void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to apply a mode change to a user or channel
|
* Attempts to apply a mode change to a user or channel
|
||||||
*/
|
*/
|
||||||
@ -569,10 +560,6 @@ class CoreExport ModeParser : public fakederef<ModeParser>
|
|||||||
*/
|
*/
|
||||||
std::string LastParse;
|
std::string LastParse;
|
||||||
|
|
||||||
unsigned int sent[256];
|
|
||||||
|
|
||||||
unsigned int seq;
|
|
||||||
|
|
||||||
/** Cached mode list for use in 004 numeric
|
/** Cached mode list for use in 004 numeric
|
||||||
*/
|
*/
|
||||||
std::string Cached004ModeList;
|
std::string Cached004ModeList;
|
||||||
@ -671,14 +658,6 @@ class CoreExport ModeParser : public fakederef<ModeParser>
|
|||||||
* @return True if the ModeWatcher was deleted correctly
|
* @return True if the ModeWatcher was deleted correctly
|
||||||
*/
|
*/
|
||||||
bool DelModeWatcher(ModeWatcher* mw);
|
bool DelModeWatcher(ModeWatcher* mw);
|
||||||
/** Process a set of mode changes from a server or user.
|
|
||||||
* @param parameters The parameters of the mode change, in the format
|
|
||||||
* they would be from a MODE command.
|
|
||||||
* @param user The source of the mode change, can be a server user.
|
|
||||||
* @param flags Optional flags controlling how the mode change is processed,
|
|
||||||
* defaults to MODE_NONE.
|
|
||||||
*/
|
|
||||||
void Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags = MODE_NONE);
|
|
||||||
|
|
||||||
/** Process a list of mode changes entirely. If the mode changes do not fit into one MODE line
|
/** Process a list of mode changes entirely. If the mode changes do not fit into one MODE line
|
||||||
* then multiple MODE lines are generated.
|
* then multiple MODE lines are generated.
|
||||||
|
@ -709,7 +709,7 @@ class CoreExport Module : public classbase, public usecountbase
|
|||||||
*/
|
*/
|
||||||
virtual void OnUserPostNick(User* user, const std::string &oldnick);
|
virtual void OnUserPostNick(User* user, const std::string &oldnick);
|
||||||
|
|
||||||
/** Called before any mode change, to allow a single access check for
|
/** Called before a mode change via the MODE command, to allow a single access check for
|
||||||
* a full mode change (use OnRawMode to check individual modes)
|
* a full mode change (use OnRawMode to check individual modes)
|
||||||
*
|
*
|
||||||
* Returning MOD_RES_ALLOW will skip prefix level checks, but can be overridden by
|
* Returning MOD_RES_ALLOW will skip prefix level checks, but can be overridden by
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
* InspIRCd -- Internet Relay Chat Daemon
|
* InspIRCd -- Internet Relay Chat Daemon
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
|
* Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
|
||||||
|
* Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
|
||||||
|
* Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
|
||||||
|
* Copyright (C) 2004-2008 Craig Edwards <craigedwards@brainbox.cc>
|
||||||
*
|
*
|
||||||
* This file is part of InspIRCd. InspIRCd is free software: you can
|
* 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
|
* redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
@ -21,13 +24,76 @@
|
|||||||
|
|
||||||
CommandMode::CommandMode(Module* parent)
|
CommandMode::CommandMode(Module* parent)
|
||||||
: Command(parent, "MODE", 1)
|
: Command(parent, "MODE", 1)
|
||||||
|
, seq(0)
|
||||||
{
|
{
|
||||||
syntax = "<target> <modes> {<mode-parameters>}";
|
syntax = "<target> <modes> {<mode-parameters>}";
|
||||||
|
memset(&sent, 0, sizeof(sent));
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdResult CommandMode::Handle(const std::vector<std::string>& parameters, User* user)
|
CmdResult CommandMode::Handle(const std::vector<std::string>& parameters, User* user)
|
||||||
{
|
{
|
||||||
ServerInstance->Modes->Process(parameters, user, (IS_LOCAL(user) ? ModeParser::MODE_NONE : ModeParser::MODE_LOCALONLY));
|
const std::string& target = parameters[0];
|
||||||
|
Channel* targetchannel = ServerInstance->FindChan(target);
|
||||||
|
User* targetuser = NULL;
|
||||||
|
if (!targetchannel)
|
||||||
|
{
|
||||||
|
if (IS_LOCAL(user))
|
||||||
|
targetuser = ServerInstance->FindNickOnly(target);
|
||||||
|
else
|
||||||
|
targetuser = ServerInstance->FindNick(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
|
||||||
|
{
|
||||||
|
user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target.c_str());
|
||||||
|
return CMD_FAILURE;
|
||||||
|
}
|
||||||
|
if (parameters.size() == 1)
|
||||||
|
{
|
||||||
|
this->DisplayCurrentModes(user, targetuser, targetchannel);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate a temporary Modes::ChangeList with the parameters
|
||||||
|
Modes::ChangeList changelist;
|
||||||
|
ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
|
||||||
|
ServerInstance->Modes.ModeParamsToChangeList(user, type, parameters, changelist);
|
||||||
|
|
||||||
|
ModResult MOD_RESULT;
|
||||||
|
FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
|
||||||
|
|
||||||
|
ModeParser::ModeProcessFlag flags = ModeParser::MODE_NONE;
|
||||||
|
if (IS_LOCAL(user))
|
||||||
|
{
|
||||||
|
if (MOD_RESULT == MOD_RES_PASSTHRU)
|
||||||
|
{
|
||||||
|
if ((targetuser) && (user != targetuser))
|
||||||
|
{
|
||||||
|
// Local users may only change the modes of other users if a module explicitly allows it
|
||||||
|
user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't change mode for other users");
|
||||||
|
return CMD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a mode change by a local user and modules didn't explicitly allow/deny.
|
||||||
|
// Ensure access checks will happen for each mode being changed.
|
||||||
|
flags |= ModeParser::MODE_CHECKACCESS;
|
||||||
|
}
|
||||||
|
else if (MOD_RESULT == MOD_RES_DENY)
|
||||||
|
return CMD_FAILURE; // Entire mode change denied by a module
|
||||||
|
}
|
||||||
|
else
|
||||||
|
flags |= ModeParser::MODE_LOCALONLY;
|
||||||
|
|
||||||
|
ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
|
||||||
|
|
||||||
|
if ((ServerInstance->Modes.GetLastParse().empty()) && (targetchannel) && (parameters.size() == 2))
|
||||||
|
{
|
||||||
|
/* Special case for displaying the list for listmodes,
|
||||||
|
* e.g. MODE #chan b, or MODE #chan +b without a parameter
|
||||||
|
*/
|
||||||
|
this->DisplayListModes(user, targetchannel, parameters[1]);
|
||||||
|
}
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,3 +101,55 @@ RouteDescriptor CommandMode::GetRouting(User* user, const std::vector<std::strin
|
|||||||
{
|
{
|
||||||
return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
|
return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
|
||||||
|
{
|
||||||
|
seq++;
|
||||||
|
|
||||||
|
for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i)
|
||||||
|
{
|
||||||
|
unsigned char mletter = *i;
|
||||||
|
if (mletter == '+')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL);
|
||||||
|
if (!mh || !mh->IsListMode())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Ensure the user doesnt request the same mode twice,
|
||||||
|
* so they can't flood themselves off out of idiocy.
|
||||||
|
*/
|
||||||
|
if (sent[mletter] == seq)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sent[mletter] = seq;
|
||||||
|
ServerInstance->Modes.ShowListModeList(user, chan, mh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandMode::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
|
||||||
|
{
|
||||||
|
if (targetchannel)
|
||||||
|
{
|
||||||
|
// Display channel's current mode string
|
||||||
|
user->WriteNumeric(RPL_CHANNELMODEIS, "%s +%s", targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
|
||||||
|
user->WriteNumeric(RPL_CHANNELCREATED, "%s %lu", targetchannel->name.c_str(), (unsigned long)targetchannel->age);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (targetuser == user || user->HasPrivPermission("users/auspex"))
|
||||||
|
{
|
||||||
|
// Display user's current mode string
|
||||||
|
user->WriteNumeric(RPL_UMODEIS, ":+%s", targetuser->FormatModes());
|
||||||
|
if (targetuser->IsOper())
|
||||||
|
{
|
||||||
|
ModeHandler* snomask = ServerInstance->Modes->FindMode('s', MODETYPE_USER);
|
||||||
|
user->WriteNumeric(RPL_SNOMASKIS, "%s :Server notice mask", snomask->GetUserParameter(user).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't view modes for other users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -64,6 +64,23 @@ class CommandAway : public Command
|
|||||||
|
|
||||||
class CommandMode : public Command
|
class CommandMode : public Command
|
||||||
{
|
{
|
||||||
|
unsigned int sent[256];
|
||||||
|
unsigned int seq;
|
||||||
|
|
||||||
|
/** Show the list of one or more list modes to a user.
|
||||||
|
* @param user User to send to.
|
||||||
|
* @param chan Channel whose lists to show.
|
||||||
|
* @param mode_sequence Mode letters to show the lists of.
|
||||||
|
*/
|
||||||
|
void DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence);
|
||||||
|
|
||||||
|
/** Show the current modes of a channel or a user to a user.
|
||||||
|
* @param user User to show the modes to.
|
||||||
|
* @param targetuser User whose modes to show. NULL if showing the modes of a channel.
|
||||||
|
* @param targetchannel Channel whose modes to show. NULL if showing the modes of a user.
|
||||||
|
*/
|
||||||
|
void DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructor for mode.
|
/** Constructor for mode.
|
||||||
*/
|
*/
|
||||||
|
125
src/mode.cpp
125
src/mode.cpp
@ -152,36 +152,6 @@ void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModeParser::DisplayCurrentModes(User* user, User* targetuser, Channel* targetchannel)
|
|
||||||
{
|
|
||||||
if (targetchannel)
|
|
||||||
{
|
|
||||||
/* Display channel's current mode string */
|
|
||||||
user->WriteNumeric(RPL_CHANNELMODEIS, "%s +%s", targetchannel->name.c_str(), targetchannel->ChanModes(targetchannel->HasUser(user)));
|
|
||||||
user->WriteNumeric(RPL_CHANNELCREATED, "%s %lu", targetchannel->name.c_str(), (unsigned long)targetchannel->age);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (targetuser == user || user->HasPrivPermission("users/auspex"))
|
|
||||||
{
|
|
||||||
/* Display user's current mode string */
|
|
||||||
user->WriteNumeric(RPL_UMODEIS, ":+%s", targetuser->FormatModes());
|
|
||||||
if ((targetuser->IsOper()))
|
|
||||||
{
|
|
||||||
ModeHandler* snomask = FindMode('s', MODETYPE_USER);
|
|
||||||
user->WriteNumeric(RPL_SNOMASKIS, "%s :Server notice mask", snomask->GetUserParameter(user).c_str());
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't view modes for other users");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank, char PrefixChar)
|
PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank, char PrefixChar)
|
||||||
: ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX)
|
: ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX)
|
||||||
, prefix(PrefixChar), prefixrank(Rank)
|
, prefix(PrefixChar), prefixrank(Rank)
|
||||||
@ -360,71 +330,6 @@ ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, Mode
|
|||||||
return MODEACTION_ALLOW;
|
return MODEACTION_ALLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModeParser::Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags)
|
|
||||||
{
|
|
||||||
const std::string& target = parameters[0];
|
|
||||||
Channel* targetchannel = ServerInstance->FindChan(target);
|
|
||||||
User* targetuser = NULL;
|
|
||||||
if (!targetchannel)
|
|
||||||
{
|
|
||||||
if (IS_LOCAL(user))
|
|
||||||
targetuser = ServerInstance->FindNickOnly(target);
|
|
||||||
else
|
|
||||||
targetuser = ServerInstance->FindNick(target);
|
|
||||||
}
|
|
||||||
ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER;
|
|
||||||
|
|
||||||
LastParse.clear();
|
|
||||||
LastChangeList.clear();
|
|
||||||
|
|
||||||
if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser))))
|
|
||||||
{
|
|
||||||
user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", target.c_str());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (parameters.size() == 1)
|
|
||||||
{
|
|
||||||
this->DisplayCurrentModes(user, targetuser, targetchannel);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate a temporary Modes::ChangeList with the parameters
|
|
||||||
Modes::ChangeList changelist;
|
|
||||||
ModeParamsToChangeList(user, type, parameters, changelist);
|
|
||||||
|
|
||||||
ModResult MOD_RESULT;
|
|
||||||
FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, changelist));
|
|
||||||
|
|
||||||
if (IS_LOCAL(user))
|
|
||||||
{
|
|
||||||
if (MOD_RESULT == MOD_RES_PASSTHRU)
|
|
||||||
{
|
|
||||||
if ((targetuser) && (user != targetuser))
|
|
||||||
{
|
|
||||||
// Local users may only change the modes of other users if a module explicitly allows it
|
|
||||||
user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't change mode for other users");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a mode change by a local user and modules didn't explicitly allow/deny.
|
|
||||||
// Ensure access checks will happen for each mode being changed.
|
|
||||||
flags |= MODE_CHECKACCESS;
|
|
||||||
}
|
|
||||||
else if (MOD_RESULT == MOD_RES_DENY)
|
|
||||||
return; // Entire mode change denied by a module
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessSingle(user, targetchannel, targetuser, changelist, flags);
|
|
||||||
|
|
||||||
if ((LastParse.empty()) && (targetchannel) && (parameters.size() == 2))
|
|
||||||
{
|
|
||||||
/* Special case for displaying the list for listmodes,
|
|
||||||
* e.g. MODE #chan b, or MODE #chan +b without a parameter
|
|
||||||
*/
|
|
||||||
this->DisplayListModes(user, targetchannel, parameters[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModeParser::ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex, unsigned int endindex)
|
void ModeParser::ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex, unsigned int endindex)
|
||||||
{
|
{
|
||||||
if (endindex > parameters.size())
|
if (endindex > parameters.size())
|
||||||
@ -580,33 +485,6 @@ unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User*
|
|||||||
return modes_processed;
|
return modes_processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModeParser::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
|
|
||||||
{
|
|
||||||
seq++;
|
|
||||||
|
|
||||||
for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)
|
|
||||||
{
|
|
||||||
unsigned char mletter = *letter;
|
|
||||||
if (mletter == '+')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Ensure the user doesnt request the same mode twice,
|
|
||||||
* so they cant flood themselves off out of idiocy.
|
|
||||||
*/
|
|
||||||
if (sent[mletter] == seq)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sent[mletter] = seq;
|
|
||||||
|
|
||||||
ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL);
|
|
||||||
|
|
||||||
if (!mh || !mh->IsListMode())
|
|
||||||
return;
|
|
||||||
|
|
||||||
ShowListModeList(user, chan, mh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ModeParser::ShowListModeList(User* user, Channel* chan, ModeHandler* mh)
|
void ModeParser::ShowListModeList(User* user, Channel* chan, ModeHandler* mh)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -1043,9 +921,6 @@ ModeParser::ModeParser()
|
|||||||
/* Clear mode handler list */
|
/* Clear mode handler list */
|
||||||
memset(modehandlers, 0, sizeof(modehandlers));
|
memset(modehandlers, 0, sizeof(modehandlers));
|
||||||
memset(modehandlersbyid, 0, sizeof(modehandlersbyid));
|
memset(modehandlersbyid, 0, sizeof(modehandlersbyid));
|
||||||
|
|
||||||
seq = 0;
|
|
||||||
memset(&sent, 0, sizeof(sent));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ModeParser::~ModeParser()
|
ModeParser::~ModeParser()
|
||||||
|
@ -52,11 +52,27 @@ class CommandSamode : public Command
|
|||||||
if (!user->HasPrivPermission("users/samode-usermodes", true))
|
if (!user->HasPrivPermission("users/samode-usermodes", true))
|
||||||
return CMD_FAILURE;
|
return CMD_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: Make ModeParser clear LastParse
|
||||||
|
Modes::ChangeList emptychangelist;
|
||||||
|
ServerInstance->Modes->ProcessSingle(ServerInstance->FakeClient, NULL, ServerInstance->FakeClient, emptychangelist);
|
||||||
|
|
||||||
this->active = true;
|
this->active = true;
|
||||||
ServerInstance->Parser.CallHandler("MODE", parameters, user);
|
CmdResult result = ServerInstance->Parser.CallHandler("MODE", parameters, user);
|
||||||
if (ServerInstance->Modes->GetLastParse().length())
|
|
||||||
ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " +ServerInstance->Modes->GetLastParse());
|
|
||||||
this->active = false;
|
this->active = false;
|
||||||
|
|
||||||
|
if (result == CMD_SUCCESS)
|
||||||
|
{
|
||||||
|
// If lastparse is empty and the MODE command handler returned CMD_SUCCESS then
|
||||||
|
// the client queried the list of a listmode (e.g. /SAMODE #chan b), which was
|
||||||
|
// handled internally by the MODE command handler.
|
||||||
|
//
|
||||||
|
// Viewing the modes of a user or a channel can also result in CMD_SUCCESS, but
|
||||||
|
// that is not possible with /SAMODE because we require at least 2 parameters.
|
||||||
|
const std::string& lastparse = ServerInstance->Modes.GetLastParse();
|
||||||
|
ServerInstance->SNO->WriteGlobalSno('a', user->nick + " used SAMODE: " + (lastparse.empty() ? irc::stringjoiner(parameters) : lastparse));
|
||||||
|
}
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user