X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmode.cpp;h=d9dba196d4419ff9e3fe9f575ec948811b3ad8ac;hb=5bea41d726f9c93ca1914ae9b6259765991d383c;hp=5f1f8225cf15d2e7766bb5b89550b658a8a348a8;hpb=117f19aec0cdadf9a106c3e5db8f7c3d0d8bbbc9;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/mode.cpp b/src/mode.cpp index 5f1f8225c..d9dba196d 100644 --- a/src/mode.cpp +++ b/src/mode.cpp @@ -390,6 +390,7 @@ void ModeParser::Process(const std::vector& 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& 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& parameters, Modes::ChangeList& changelist) +{ const std::string& mode_sequence = parameters[1]; bool adding = true; @@ -437,29 +451,10 @@ void ModeParser::Process(const std::vector& 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 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 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); } }