#include "inspircd.h"
-int InspIRCd::PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype)
+bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype)
{
ModResult res;
FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype));
/* Module matched */
if (res == MOD_RES_ALLOW)
- return 0;
+ return true;
/* Module explicitly didnt match */
if (res == MOD_RES_DENY)
- return 1;
+ return false;
/* We dont handle any hash types except for plaintext - Thanks tra26 */
if (!hashtype.empty() && hashtype != "plaintext")
- /* See below. 1 because they dont match */
- return 1;
+ return false;
- return (data != input); // this seems back to front, but returns 0 if they *match*, 1 else
+ return TimingSafeCompare(data, input);
}
-/* LoopCall is used to call a command classes handler repeatedly based on the contents of a comma seperated list.
- * There are two overriden versions of this method, one of which takes two potential lists and the other takes one.
- * We need a version which takes two potential lists for JOIN, because a JOIN may contain two lists of items at once,
- * the channel names and their keys as follows:
- * JOIN #chan1,#chan2,#chan3 key1,,key3
- * Therefore, we need to deal with both lists concurrently. The first instance of this method does that by creating
- * two instances of irc::commasepstream and reading them both together until the first runs out of tokens.
- * The second version is much simpler and just has the one stream to read, and is used in NAMES, WHOIS, PRIVMSG etc.
- * Both will only parse until they reach ServerInstance->Config->MaxTargets number of targets, to stop abuse via spam.
- */
-int CommandParser::LoopCall(User* user, Command* CommandObj, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
+bool CommandParser::LoopCall(User* user, Command* handler, const std::vector<std::string>& parameters, unsigned int splithere, int extra, bool usemax)
{
if (splithere >= parameters.size())
- return 0;
+ return false;
- if (extra >= (signed)parameters.size())
- extra = -1;
-
- /* First check if we have more than one item in the list, if we don't we return zero here and the handler
+ /* First check if we have more than one item in the list, if we don't we return false here and the handler
* which called us just carries on as it was.
*/
if (parameters[splithere].find(',') == std::string::npos)
- return 0;
+ return false;
/** Some lame ircds will weed out dupes using some shitty O(n^2) algorithm.
* By using std::set (thanks for the idea w00t) we can cut this down a ton.
* ...VOOODOOOO!
+ *
+ * Only check for duplicates if there is one list (allow them in JOIN).
*/
- std::set<irc::string> dupes;
+ insp::flat_set<irc::string> dupes;
+ bool check_dupes = (extra < 0);
- /* Create two lists, one for channel names, one for keys
+ /* Create two sepstreams, if we have only one list, then initialize the second sepstream with
+ * an empty string. The second parameter of the constructor of the sepstream tells whether
+ * or not to allow empty tokens.
+ * We allow empty keys, so "JOIN #a,#b ,bkey" will be interpreted as "JOIN #a", "JOIN #b bkey"
*/
irc::commasepstream items1(parameters[splithere]);
- irc::commasepstream items2(extra >= 0 ? parameters[extra] : "");
- std::string extrastuff;
+ irc::commasepstream items2(extra >= 0 ? parameters[extra] : "", true);
std::string item;
unsigned int max = 0;
+ LocalUser* localuser = IS_LOCAL(user);
- /* Attempt to iterate these lists and call the command objech
- * which called us, for every parameter pair until there are
- * no more left to parse.
+ /* Attempt to iterate these lists and call the command handler
+ * for every parameter or parameter pair until there are no more
+ * left to parse.
*/
while (items1.GetToken(item) && (!usemax || max++ < ServerInstance->Config->MaxTargets))
{
- if (dupes.find(item.c_str()) == dupes.end())
+ if ((!check_dupes) || (dupes.insert(item.c_str()).second))
{
std::vector<std::string> new_parameters(parameters);
-
- if (!items2.GetToken(extrastuff))
- extrastuff.clear();
-
new_parameters[splithere] = item;
- if (extra >= 0)
- new_parameters[extra] = extrastuff;
-
- CommandObj->Handle(new_parameters, user);
-
- dupes.insert(item.c_str());
- }
- }
- return 1;
-}
-
-bool CommandParser::IsValidCommand(const std::string &commandname, unsigned int pcnt, User * user)
-{
- Commandtable::iterator n = cmdlist.find(commandname);
- if (n != cmdlist.end())
- {
- if ((pcnt >= n->second->min_params))
- {
- if (IS_LOCAL(user) && n->second->flags_needed)
+ if (extra >= 0)
{
- if (user->IsModeSet(n->second->flags_needed))
- {
- return (user->HasPermission(commandname));
- }
+ // If we have two lists then get the next item from the second list.
+ // In case it runs out of elements then 'item' will be an empty string.
+ items2.GetToken(item);
+ new_parameters[extra] = item;
}
- else
+
+ CmdResult result = handler->Handle(new_parameters, user);
+ if (localuser)
{
- return true;
+ // Run the OnPostCommand hook with the last parameter (original line) being empty
+ // to indicate that the command had more targets in its original form.
+ item.clear();
+ FOREACH_MOD(OnPostCommand, (handler, new_parameters, localuser, result, item));
}
}
}
- return false;
+
+ return true;
}
Command* CommandParser::GetHandler(const std::string &commandname)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
return n->second;
// calls a handler function for a command
-CmdResult CommandParser::CallHandler(const std::string &commandname, const std::vector<std::string>& parameters, User *user)
+CmdResult CommandParser::CallHandler(const std::string& commandname, const std::vector<std::string>& parameters, User* user, Command** cmd)
{
- Commandtable::iterator n = cmdlist.find(commandname);
+ CommandMap::iterator n = cmdlist.find(commandname);
if (n != cmdlist.end())
{
if (bOkay)
{
+ if (cmd)
+ *cmd = n->second;
return n->second->Handle(parameters,user);
}
}
/* find the command, check it exists */
Command* handler = GetHandler(command);
+ // Penalty to give if the command fails before the handler is executed
+ unsigned int failpenalty = 0;
+
/* Modify the user's penalty regardless of whether or not the command exists */
if (!user->HasPrivPermission("users/flood/no-throttle"))
{
// If it *doesn't* exist, give it a slightly heftier penalty than normal to deter flooding us crap
- user->CommandFloodPenalty += handler ? handler->Penalty * 1000 : 2000;
+ unsigned int penalty = (handler ? handler->Penalty * 1000 : 2000);
+ user->CommandFloodPenalty += penalty;
+
+ // Increase their penalty later if we fail and the command has 0 penalty by default (i.e. in Command::Penalty) to
+ // throttle sending ERR_* from the command parser. If the command does have a non-zero penalty then this is not
+ // needed because we've increased their penalty above.
+ if (penalty == 0)
+ failpenalty = 1000;
}
if (!handler)
if (!handler)
{
if (user->registered == REG_ALL)
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
- ServerInstance->stats->statsUnknown++;
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command",command.c_str());
+ ServerInstance->stats.Unknown++;
return;
}
}
{
if (!user->IsModeSet(handler->flags_needed))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - You do not have the required operator privileges",user->nick.c_str());
+ user->CommandFloodPenalty += failpenalty;
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges");
return;
}
if (!user->HasPermission(command))
{
- user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to command %s",
- user->nick.c_str(), user->oper->name.c_str(), command.c_str());
+ user->CommandFloodPenalty += failpenalty;
+ user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to command %s",
+ user->oper->name.c_str(), command.c_str());
return;
}
}
if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled()))
{
/* command is disabled! */
+ user->CommandFloodPenalty += failpenalty;
if (ServerInstance->Config->DisabledDontExist)
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :Unknown command",user->nick.c_str(),command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command", command.c_str());
}
else
{
- user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s %s :This command has been disabled.",
- user->nick.c_str(), command.c_str());
+ user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", command.c_str());
}
- ServerInstance->SNO->WriteToSnoMask('t', "%s denied for %s (%s@%s)",
+ ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)",
command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str());
return;
}
if (command_p.size() < handler->min_params)
{
- user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s %s :Not enough parameters.", user->nick.c_str(), command.c_str());
+ user->CommandFloodPenalty += failpenalty;
+ user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s :Not enough parameters.", command.c_str());
if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length()))
- user->WriteNumeric(RPL_SYNTAX, "%s :SYNTAX %s %s", user->nick.c_str(), handler->name.c_str(), handler->syntax.c_str());
+ user->WriteNumeric(RPL_SYNTAX, ":SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str());
return;
}
if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg()))
{
+ user->CommandFloodPenalty += failpenalty;
user->WriteNumeric(ERR_NOTREGISTERED, "%s :You have not registered",command.c_str());
}
else
*/
CmdResult result = handler->Handle(command_p, user);
- FOREACH_MOD(I_OnPostCommand, OnPostCommand(handler, command_p, user, result, cmd));
+ FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, cmd));
}
}
void CommandParser::RemoveCommand(Command* x)
{
- Commandtable::iterator n = cmdlist.find(x->name);
+ CommandMap::iterator n = cmdlist.find(x->name);
if (n != cmdlist.end() && n->second == x)
cmdlist.erase(n);
}
+CommandBase::CommandBase(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : ServiceProvider(mod, cmd, SERVICE_COMMAND)
+ , flags_needed(0)
+ , min_params(minpara)
+ , max_params(maxpara)
+ , use_count(0)
+ , disabled(false)
+ , works_before_reg(false)
+ , allow_empty_last_param(true)
+ , Penalty(1)
+{
+}
+
+CommandBase::~CommandBase()
+{
+}
+
+void CommandBase::EncodeParameter(std::string& parameter, int index)
+{
+}
+
+RouteDescriptor CommandBase::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+ return ROUTE_LOCALONLY;
+}
+
+Command::Command(Module* mod, const std::string& cmd, unsigned int minpara, unsigned int maxpara)
+ : CommandBase(mod, cmd, minpara, maxpara)
+ , force_manual_route(false)
+{
+}
+
Command::~Command()
{
- ServerInstance->Parser->RemoveCommand(this);
+ ServerInstance->Parser.RemoveCommand(this);
}
void CommandParser::ProcessBuffer(std::string &buffer,LocalUser *user)
{
- if (!user || buffer.empty())
+ if (buffer.empty())
return;
ServerInstance->Logs->Log("USERINPUT", LOG_RAWIO, "C[%s] I :%s %s",
{
}
-int CommandParser::TranslateUIDs(const std::vector<TranslateType> to, const std::vector<std::string> &source, std::string &dest, bool prefix_final, Command* custom_translator)
+std::string CommandParser::TranslateUIDs(const std::vector<TranslateType>& to, const std::vector<std::string>& source, bool prefix_final, CommandBase* custom_translator)
{
std::vector<TranslateType>::const_iterator types = to.begin();
- User* user = NULL;
- unsigned int i;
- int translations = 0;
- dest.clear();
+ std::string dest;
- for(i=0; i < source.size(); i++)
+ for (unsigned int i = 0; i < source.size(); i++)
{
- TranslateType t;
- std::string item = source[i];
-
- if (types == to.end())
- t = TR_TEXT;
- else
+ TranslateType t = TR_TEXT;
+ // They might supply less translation types than parameters,
+ // in that case pretend that all remaining types are TR_TEXT
+ if (types != to.end())
{
t = *types;
types++;
}
- if (prefix_final && i == source.size() - 1)
- dest.append(":");
+ bool last = (i == (source.size() - 1));
+ if (prefix_final && last)
+ dest.push_back(':');
- switch (t)
- {
- case TR_NICK:
- /* Translate single nickname */
- user = ServerInstance->FindNick(item);
- if (user)
- {
- dest.append(user->uuid);
- translations++;
- }
- else
- dest.append(item);
- break;
- case TR_CUSTOM:
- if (custom_translator)
- custom_translator->EncodeParameter(item, i);
- dest.append(item);
- break;
- case TR_END:
- case TR_TEXT:
- default:
- /* Do nothing */
- dest.append(item);
- break;
- }
- if (i != source.size() - 1)
- dest.append(" ");
+ TranslateSingleParam(t, source[i], dest, custom_translator, i);
+
+ if (!last)
+ dest.push_back(' ');
}
- return translations;
+ return dest;
}
-int CommandParser::TranslateUIDs(TranslateType to, const std::string &source, std::string &dest)
+void CommandParser::TranslateSingleParam(TranslateType to, const std::string& item, std::string& dest, CommandBase* custom_translator, unsigned int paramnumber)
{
- User* user = NULL;
- int translations = 0;
- dest.clear();
-
switch (to)
{
case TR_NICK:
+ {
/* Translate single nickname */
- user = ServerInstance->FindNick(source);
+ User* user = ServerInstance->FindNick(item);
if (user)
+ dest.append(user->uuid);
+ else
+ dest.append(item);
+ break;
+ }
+ case TR_CUSTOM:
+ {
+ if (custom_translator)
{
- dest = user->uuid;
- translations++;
+ std::string translated = item;
+ custom_translator->EncodeParameter(translated, paramnumber);
+ dest.append(translated);
+ break;
}
- else
- dest = source;
- break;
- case TR_END:
+ // If no custom translator was given, fall through
+ }
case TR_TEXT:
default:
/* Do nothing */
- dest = source;
+ dest.append(item);
break;
}
-
- return translations;
}