]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/coremods/core_user/cmd_mode.cpp
Merge pull request #976 from SaberUK/master+fix-xline-db
[user/henk/code/inspircd.git] / src / coremods / core_user / cmd_mode.cpp
index d2e9b4f63016a0f6fe47f7e9c4af522e6d052284..190983d13047b28d9f2cd6568d3d9ae933148d2b 100644 (file)
@@ -1,8 +1,10 @@
 /*
  * InspIRCd -- Internet Relay Chat Daemon
  *
- *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
- *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
+ *   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
  * redistribute it and/or modify it under the terms of the GNU General Public
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include "inspircd.h"
+#include "core_user.h"
 
-/** Handle /MODE.
- */
-class CommandMode : public Command
+CommandMode::CommandMode(Module* parent)
+       : Command(parent, "MODE", 1)
+       , seq(0)
+{
+       syntax = "<target> <modes> {<mode-parameters>}";
+       memset(&sent, 0, sizeof(sent));
+}
+
+CmdResult CommandMode::Handle(const std::vector<std::string>& parameters, User* user)
 {
- public:
-       /** Constructor for mode.
-        */
-       CommandMode ( Module* parent) : Command(parent,"MODE",1) { syntax = "<target> <modes> {<mode-parameters>}"; }
-       /** Handle command.
-        * @param parameters The parameters to the command
-        * @param user The user issuing the command
-        * @return A value from CmdResult to indicate command success or failure.
-        */
-       CmdResult Handle(const std::vector<std::string>& parameters, User *user);
-       RouteDescriptor GetRouting(User* user, const std::vector<std::string>& parameters)
+       const std::string& target = parameters[0];
+       Channel* targetchannel = ServerInstance->FindChan(target);
+       User* targetuser = NULL;
+       if (!targetchannel)
        {
-               return (IS_LOCAL(user) ? ROUTE_LOCALONLY : ROUTE_BROADCAST);
+               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;
+
+       if (IS_LOCAL(user))
+               ServerInstance->Modes->ProcessSingle(user, targetchannel, targetuser, changelist, flags);
+       else
+               ServerInstance->Modes->Process(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]);
+       }
 
-/** Handle /MODE
- */
-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));
        return CMD_SUCCESS;
 }
 
+RouteDescriptor CommandMode::GetRouting(User* user, const std::vector<std::string>& parameters)
+{
+       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;
 
-COMMAND_INIT(CommandMode)
+               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
+                       // XXX: Use WriteServ() because WriteNumeric() assumes the target (i.e. next word after the number)
+                       // is 'user' and puts his nick there which is not what we want
+                       user->WriteServ("%03d %s :+%s", RPL_UMODEIS, targetuser->nick.c_str(), targetuser->FormatModes());
+                       if (targetuser->IsOper())
+                       {
+                               ModeHandler* snomask = ServerInstance->Modes->FindMode('s', MODETYPE_USER);
+                               std::string snomaskstr = snomask->GetUserParameter(user);
+                               // snomaskstr is empty if the snomask mode isn't set, otherwise it begins with a '+'.
+                               // In the former case output a "+", not an empty string.
+                               user->WriteServ("%03d %s %s%s :Server notice mask", RPL_SNOMASKIS, targetuser->nick.c_str(), (snomaskstr.empty() ? "+" : ""), snomaskstr.c_str());
+                       }
+               }
+               else
+               {
+                       user->WriteNumeric(ERR_USERSDONTMATCH, ":Can't view modes for other users");
+               }
+       }
+}