// 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(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags) { std::string target = parameters[0]; Channel* targetchannel = ServerInstance->FindChan(target); User* targetuser = ServerInstance->FindNick(target); ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER; LastParse.clear(); LastParseParams.clear(); LastParseTranslate.clear(); if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser)))) { user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(),target.c_str()); return; } if (parameters.size() == 1) { this->DisplayCurrentModes(user, targetuser, targetchannel, target.c_str()); return; } ModResult MOD_RESULT; FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, parameters)); bool SkipAccessChecks = false; if (!IS_LOCAL(user) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW) SkipAccessChecks = true; else if (MOD_RESULT == MOD_RES_DENY) return; if (targetuser && !SkipAccessChecks && user != targetuser) { user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't change mode for other users", user->nick.c_str()); return; } std::string mode_sequence = parameters[1]; std::string output_mode; std::ostringstream output_parameters; LastParseParams.push_back(output_mode); LastParseTranslate.push_back(TR_TEXT); bool adding = true; char output_pm = '\0'; // current output state, '+' or '-' unsigned int param_at = 2; for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++) { unsigned char modechar = *letter; if (modechar == '+' || modechar == '-') { adding = (modechar == '+'); continue; } ModeHandler *mh = this->FindMode(modechar, type); if (!mh) { /* No mode handler? Unknown mode character then. */ user->WriteServ("%d %s %c :is unknown mode char to me", type == MODETYPE_CHANNEL ? 472 : 501, user->nick.c_str(), modechar); continue; } std::string parameter; int pcnt = mh->GetNumParams(adding); if (pcnt && param_at == parameters.size()) { /* No parameter, continue to the next mode */ mh->OnParameterMissing(user, targetuser, targetchannel); continue; } else if (pcnt) { parameter = parameters[param_at++]; /* Make sure the user isn't trying to slip in an invalid parameter */ if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos)) continue; 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; } } ModeAction ma = TryMode(user, targetuser, targetchannel, adding, modechar, parameter, SkipAccessChecks); if (ma != MODEACTION_ALLOW) continue; char needed_pm = adding ? '+' : '-'; if (needed_pm != output_pm) { output_pm = needed_pm; output_mode.append(1, output_pm); } output_mode.append(1, modechar); if (pcnt) { output_parameters << " " << parameter; LastParseParams.push_back(parameter); LastParseTranslate.push_back(mh->GetTranslateType()); } if ( (output_mode.length() + output_parameters.str().length() > 450) || (output_mode.length() > 100) || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes)) { /* mode sequence is getting too long */ break; } } LastParseParams[0] = output_mode; if (!output_mode.empty()) { LastParse = targetchannel ? targetchannel->name : targetuser->nick; LastParse.append(" "); LastParse.append(output_mode); LastParse.append(output_parameters.str()); if (!(flags & MODE_LOCALONLY)) ServerInstance->PI->SendMode(user, targetuser, targetchannel, LastParseParams, LastParseTranslate); if (targetchannel) targetchannel->WriteChannel(user, "MODE " + LastParse); else targetuser->WriteFrom(user, "MODE " + LastParse); FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastParseParams, LastParseTranslate)); } else if (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); } }