+
+ changelist.push(mh, adding, parameter);
+ }
+}
+
+static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuser, const Modes::Change& item)
+{
+ // An empty parameter is never acceptable
+ if (item.param.empty())
+ {
+ item.mh->OnParameterMissing(user, targetuser, targetchannel);
+ return false;
+ }
+
+ // The parameter cannot begin with a ':' character or contain a space
+ if ((item.param[0] == ':') || (item.param.find(' ') != std::string::npos))
+ return false;
+
+ return true;
+}
+
+// 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()+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)))