]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/mode.cpp
Use Modes::ChangeList in ModeHandler::RemoveMode()
[user/henk/code/inspircd.git] / src / mode.cpp
index 5f1f8225cf15d2e7766bb5b89550b658a8a348a8..d9dba196d4419ff9e3fe9f575ec948811b3ad8ac 100644 (file)
@@ -390,6 +390,7 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User* user,
 
        // 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, parameters));
@@ -413,6 +414,19 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User* user,
                        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)
+{
        const std::string& mode_sequence = parameters[1];
 
        bool adding = true;
@@ -437,29 +451,10 @@ void ModeParser::Process(const std::vector<std::string>& parameters, User* user,
 
                std::string parameter;
                if (mh->GetNumParams(adding) && param_at < parameters.size())
-               {
                        parameter = parameters[param_at++];
-                       if ((flags & MODE_MERGE) && targetchannel && targetchannel->IsModeSet(mh) && !mh->IsListMode())
-                       {
-                               std::string ours = targetchannel->GetModeParameter(mh);
-                               if (!mh->ResolveModeConflict(parameter, ours, targetchannel))
-                                       /* we won the mode merge, don't apply this mode */
-                                       continue;
-                       }
-               }
 
                changelist.push(mh, adding, parameter);
        }
-
-       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, mode_sequence);
-       }
 }
 
 static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuser, const Modes::Change& item)
@@ -478,27 +473,61 @@ static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuse
        return true;
 }
 
-void ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags)
+// Returns true if we should apply a merged mode, false if we should skip it
+static bool ShouldApplyMergedMode(Channel* chan, Modes::Change& item)
+{
+       ModeHandler* mh = item.mh;
+       if ((!chan) || (!chan->IsModeSet(mh)) || (mh->IsListMode()))
+               // Mode not set here or merge is not applicable, apply the incoming mode
+               return true;
+
+       // Mode handler decides
+       std::string ours = chan->GetModeParameter(mh);
+       return mh->ResolveModeConflict(item.param, ours, chan);
+}
+
+void ModeParser::Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags)
+{
+       // Call ProcessSingle until the entire list is processed, but at least once to ensure
+       // LastParse and LastChangeList are cleared
+       unsigned int processed = 0;
+       do
+       {
+               unsigned int n = ProcessSingle(user, targetchannel, targetuser, changelist, flags, processed);
+               processed += n;
+       }
+       while (processed < changelist.size());
+}
+
+unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
 {
        LastParse.clear();
        LastChangeList.clear();
 
+       unsigned int modes_processed = 0;
        std::string output_mode;
        std::string output_parameters;
 
        char output_pm = '\0'; // current output state, '+' or '-'
        Modes::ChangeList::List& list = changelist.getlist();
-       for (Modes::ChangeList::List::iterator i = list.begin(); i != list.end(); ++i)
+       for (Modes::ChangeList::List::iterator i = list.begin()+beginindex; i != list.end(); ++i)
        {
+               modes_processed++;
+
                Modes::Change& item = *i;
                ModeHandler* mh = item.mh;
 
                // If the mode is supposed to have a parameter then we first take a look at item.param
+               // and, if we were asked to, also handle mode merges now
                if (mh->GetNumParams(item.adding))
                {
                        // Skip the mode if the parameter does not pass basic validation
                        if (!IsModeParamValid(user, targetchannel, targetuser, item))
                                continue;
+
+                       // If this is a merge and we won we don't apply this mode
+                       if ((flags & MODE_MERGE) && (!ShouldApplyMergedMode(targetchannel, item)))
+                               continue;
                }
 
                ModeAction ma = TryMode(user, targetuser, targetchannel, item, (!(flags & MODE_CHECKACCESS)));
@@ -544,6 +573,8 @@ void ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetu
 
                FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags, output_mode));
        }
+
+       return modes_processed;
 }
 
 void ModeParser::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence)
@@ -747,16 +778,9 @@ bool ModeParser::DelMode(ModeHandler* mh)
                                Channel* chan = i->second;
                                ++i;
 
-                               irc::modestacker stack(false);
-                               mh->RemoveMode(chan, stack);
-
-                               std::vector<std::string> stackresult;
-                               stackresult.push_back(chan->name);
-                               while (stack.GetStackedLine(stackresult))
-                               {
-                                       this->Process(stackresult, ServerInstance->FakeClient, MODE_LOCALONLY);
-                                       stackresult.erase(stackresult.begin() + 1, stackresult.end());
-                               }
+                               Modes::ChangeList changelist;
+                               mh->RemoveMode(chan, changelist);
+                               this->Process(ServerInstance->FakeClient, chan, NULL, changelist, MODE_LOCALONLY);
                        }
                }
                break;
@@ -928,33 +952,31 @@ void ModeHandler::RemoveMode(User* user)
        // Remove the mode if it's set on the user
        if (user->IsModeSet(this->GetModeChar()))
        {
-               std::vector<std::string> parameters;
-               parameters.push_back(user->nick);
-               parameters.push_back("-");
-               parameters[1].push_back(this->GetModeChar());
-               ServerInstance->Modes->Process(parameters, ServerInstance->FakeClient, ModeParser::MODE_LOCALONLY);
+               Modes::ChangeList changelist;
+               changelist.push_remove(this);
+               ServerInstance->Modes->Process(ServerInstance->FakeClient, NULL, user, changelist, ModeParser::MODE_LOCALONLY);
        }
 }
 
-void ModeHandler::RemoveMode(Channel* channel, irc::modestacker& stack)
+void ModeHandler::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
 {
        if (channel->IsModeSet(this))
        {
                if (this->GetNumParams(false))
                        // Removing this mode requires a parameter
-                       stack.Push(this->GetModeChar(), channel->GetModeParameter(this));
+                       changelist.push_remove(this, channel->GetModeParameter(this));
                else
-                       stack.Push(this->GetModeChar());
+                       changelist.push_remove(this);
        }
 }
 
-void PrefixMode::RemoveMode(Channel* chan, irc::modestacker& stack)
+void PrefixMode::RemoveMode(Channel* chan, Modes::ChangeList& changelist)
 {
        const Channel::MemberMap& userlist = chan->GetUsers();
        for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
        {
                if (i->second->hasMode(this->GetModeChar()))
-                       stack.Push(this->GetModeChar(), i->first->nick);
+                       changelist.push_remove(this, i->first->nick);
        }
 }