void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_sequence) { seq++; for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++) { unsigned char mletter = *letter; if (mletter == '+') continue; /* Ensure the user doesnt request the same mode twice, * so they cant flood themselves off out of idiocy. */ if (sent[mletter] == seq) continue; sent[mletter] = seq; ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL); if (!mh || !mh->IsListMode()) return; ModResult MOD_RESULT; FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mletter, "", true, 0)); if (MOD_RESULT == MOD_RES_DENY) continue; bool display = true; if (!user->HasPrivPermission("channels/auspex") && ServerInstance->Config->HideModeLists[mletter] && (chan->GetPrefixValue(user) < HALFOP_VALUE)) { user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You do not have access to view the +%c list", user->nick.c_str(), chan->name.c_str(), mletter); display = false; } // Ask mode watchers whether it's OK to show the list std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name); for (ModeWatchIter i = itpair.first; i != itpair.second; ++i) { ModeWatcher* mw = i->second; if (mw->GetModeType() == MODETYPE_CHANNEL) { std::string dummyparam; if (!mw->BeforeMode(user, NULL, chan, dummyparam, true)) { // A mode watcher doesn't want us to show the list display = false; break; } } } if (display) mh->DisplayList(user, chan); else mh->DisplayEmptyList(user, chan); } }
// 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); }
std::string ModeParser::GiveModeList(ModeType mt) { std::string type1; /* Listmodes EXCEPT those with a prefix */ std::string type2; /* Modes that take a param when adding or removing */ std::string type3; /* Modes that only take a param when adding */ std::string type4; /* Modes that dont take a param */ for (unsigned char mode = 'A'; mode <= 'z'; mode++) { ModeHandler* mh = modehandlers[mt][mode-65]; /* One parameter when adding */ if (mh) { if (mh->GetNumParams(true)) { PrefixMode* pm = mh->IsPrefixMode(); if ((mh->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0))) { type1 += mh->GetModeChar(); } else { /* ... and one parameter when removing */ if (mh->GetNumParams(false)) { /* But not a list mode */ if (!pm) { type2 += mh->GetModeChar(); } } else { /* No parameters when removing */ type3 += mh->GetModeChar(); } } } else { type4 += mh->GetModeChar(); } } } return type1 + "," + type2 + "," + type3 + "," + type4; }
static void DisplayList(User* user, Channel* channel) { std::stringstream items; for(char letter = 'A'; letter <= 'z'; letter++) { ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_CHANNEL); if (!mh || mh->IsListMode()) continue; if (!channel->IsModeSet(mh)) continue; items << " +" << mh->name; if (mh->GetNumParams(true)) items << " " << channel->GetModeParameter(mh); } const std::string line = ":" + ServerInstance->Config->ServerName + " 961 " + user->nick + " " + channel->name; user->SendText(line, items); user->WriteNumeric(960, "%s %s :End of mode list", user->nick.c_str(), channel->name.c_str()); }
void CommandMode::DisplayListModes(User* user, Channel* chan, const std::string& mode_sequence) { seq++; for (std::string::const_iterator i = mode_sequence.begin(); i != mode_sequence.end(); ++i) { unsigned char mletter = *i; if (mletter == '+') continue; ModeHandler* mh = ServerInstance->Modes->FindMode(mletter, MODETYPE_CHANNEL); if (!mh || !mh->IsListMode()) return; /* Ensure the user doesnt request the same mode twice, * so they can't flood themselves off out of idiocy. */ if (sent[mletter] == seq) continue; sent[mletter] = seq; ServerInstance->Modes.ShowListModeList(user, chan, mh); } }
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); } }
ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool adding, const unsigned char modechar, std::string ¶meter, bool SkipACL) { ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER; ModeHandler *mh = FindMode(modechar, type); int pcnt = mh->GetNumParams(adding); // crop mode parameter size to 250 characters if (parameter.length() > 250 && adding) parameter = parameter.substr(0, 250); ModResult MOD_RESULT; FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, modechar, parameter, adding, pcnt)); if (IS_LOCAL(user) && (MOD_RESULT == MOD_RES_DENY)) return MODEACTION_DENY; if (chan && !SkipACL && (MOD_RESULT != MOD_RES_ALLOW)) { MOD_RESULT = mh->AccessCheck(user, chan, parameter, adding); if (MOD_RESULT == MOD_RES_DENY) return MODEACTION_DENY; if (MOD_RESULT == MOD_RES_PASSTHRU) { unsigned int neededrank = mh->GetLevelRequired(); /* Compare our rank on the channel against the rank of the required prefix, * allow if >= ours. Because mIRC and xchat throw a tizz if the modes shown * in NAMES(X) are not in rank order, we know the most powerful mode is listed * first, so we don't need to iterate, we just look up the first instead. */ unsigned int ourrank = chan->GetPrefixValue(user); if (ourrank < neededrank) { PrefixMode* neededmh = NULL; for(char c='A'; c <= 'z'; c++) { PrefixMode* privmh = FindPrefixMode(c); if (privmh && privmh->GetPrefixRank() >= neededrank) { // this mode is sufficient to allow this action if (!neededmh || privmh->GetPrefixRank() < neededmh->GetPrefixRank()) neededmh = privmh; } } if (neededmh) user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have channel %s access or above to %sset channel mode %c", user->nick.c_str(), chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar); else user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You cannot %sset channel mode %c", user->nick.c_str(), chan->name.c_str(), adding ? "" : "un", modechar); return MODEACTION_DENY; } } } // Ask mode watchers whether this mode change is OK std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name); for (ModeWatchIter i = itpair.first; i != itpair.second; ++i) { ModeWatcher* mw = i->second; if (mw->GetModeType() == type) { if (!mw->BeforeMode(user, targetuser, chan, parameter, adding)) return MODEACTION_DENY; // A module whacked the parameter completely, and there was one. Abort. if (pcnt && parameter.empty()) return MODEACTION_DENY; } } if (IS_LOCAL(user) && !user->IsOper()) { char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes; if (disabled[modechar - 'A']) { user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - %s mode %c has been locked by the administrator", user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar); return MODEACTION_DENY; } } if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type)) { /* It's an oper only mode, and they don't have access to it. */ if (user->IsOper()) { user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to set %s mode %c", user->nick.c_str(), user->oper->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar); } else { user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Only operators may set %s mode %c", user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar); } return MODEACTION_DENY; } /* Call the handler for the mode */ ModeAction ma = mh->OnModeChange(user, targetuser, chan, parameter, adding); if (pcnt && parameter.empty()) return MODEACTION_DENY; if (ma != MODEACTION_ALLOW) return ma; if ((!mh->IsListMode()) && (mh->GetNumParams(true)) && (chan)) chan->SetModeParam(mh, (adding ? parameter : "")); itpair = modewatchermap.equal_range(mh->name); for (ModeWatchIter i = itpair.first; i != itpair.second; ++i) { ModeWatcher* mw = i->second; if (mw->GetModeType() == type) mw->AfterMode(user, targetuser, chan, parameter, adding); } return MODEACTION_ALLOW; }
CmdResult Handle(const std::vector<std::string> ¶meters, User *user) { ModeHandler* mh; Channel* chan = ServerInstance->FindChan(parameters[0]); char modeletter = parameters[1][0]; if (chan == NULL) { user->WriteNotice("The channel " + parameters[0] + " does not exist."); return CMD_FAILURE; } mh = ServerInstance->Modes->FindMode(modeletter, MODETYPE_CHANNEL); if (mh == NULL || parameters[1].size() > 1) { user->WriteNotice(parameters[1] + " is not a valid channel mode."); return CMD_FAILURE; } if (chan->GetPrefixValue(user) < mh->GetLevelRequired()) { user->WriteNotice("You do not have access to unset " + ConvToStr(modeletter) + " on " + chan->name + "."); return CMD_FAILURE; } unsigned int prefixrank; char prefixchar; std::string pattern = parameters.size() > 2 ? parameters[2] : "*"; ListModeBase* lm; ListModeBase::ModeList* ml; irc::modestacker modestack(false); if (!mh->IsListMode()) { if (chan->IsModeSet(mh)) modestack.Push(modeletter); } else if (((prefixrank = mh->GetPrefixRank()) && (prefixchar = mh->GetPrefix()))) { // As user prefix modes don't have a GetList() method, let's iterate through the channel's users. for (UserMembIter it = chan->userlist.begin(); it != chan->userlist.end(); ++it) { if (!InspIRCd::Match(it->first->nick, pattern)) continue; if (((strchr(chan->GetAllPrefixChars(user), prefixchar)) != NULL) && !(it->first == user && prefixrank > VOICE_VALUE)) modestack.Push(modeletter, it->first->nick); } } else if (((lm = dynamic_cast<ListModeBase*>(mh)) != NULL) && ((ml = lm->GetList(chan)) != NULL)) { for (ListModeBase::ModeList::iterator it = ml->begin(); it != ml->end(); ++it) { if (!InspIRCd::Match(it->mask, pattern)) continue; modestack.Push(modeletter, it->mask); } } else { user->WriteNotice("Could not remove channel mode " + ConvToStr(modeletter)); return CMD_FAILURE; } parameterlist stackresult; stackresult.push_back(chan->name); while (modestack.GetStackedLine(stackresult)) { ServerInstance->Modes->Process(stackresult, user); stackresult.erase(stackresult.begin() + 1, stackresult.end()); } return CMD_SUCCESS; }