// 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));
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;
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)
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)));
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)
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;
// 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);
}
}