/* returns a list of DIRECT servernames for a specific channel */ void SpanningTreeUtilities::GetListOfServersForChannel(Channel* c, TreeSocketSet& list, char status, const CUList& exempt_list) { unsigned int minrank = 0; if (status) { PrefixMode* mh = ServerInstance->Modes->FindPrefix(status); if (mh) minrank = mh->GetPrefixRank(); } const UserMembList *ulist = c->GetUsers(); for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++) { if (IS_LOCAL(i->first)) continue; if (minrank && i->second->getRank() < minrank) continue; if (exempt_list.find(i->first) == exempt_list.end()) { TreeServer* best = TreeServer::Get(i->first); list.insert(best->GetSocket()); } } return; }
ModResult Call(User* user, Channel* chan, const std::string& restriction) { unsigned int mypfx = chan->GetPrefixValue(user); std::string minmode; ListModeBase::ModeList* list = ec.GetList(chan); if (list) { for (ListModeBase::ModeList::iterator i = list->begin(); i != list->end(); ++i) { std::string::size_type pos = (*i).mask.find(':'); if (pos == std::string::npos) continue; if ((*i).mask.substr(0,pos) == restriction) minmode = (*i).mask.substr(pos + 1); } } PrefixMode* mh = FindMode(minmode); if (mh && mypfx >= mh->GetPrefixRank()) return MOD_RES_ALLOW; if (mh || minmode == "*") return MOD_RES_DENY; return ServerInstance->HandleOnCheckExemption.Call(user, chan, restriction); }
CmdResult Handle(User* user, const Params& parameters) override { Channel* channel = ServerInstance->FindChan(parameters[0]); if (!channel) { user->WriteNumeric(Numerics::NoSuchChannel(parameters[0])); return CMD_FAILURE; } unsigned int cm = channel->GetPrefixValue(user); if (cm < HALFOP_VALUE) { user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel"); return CMD_FAILURE; } TimedBan T; unsigned long duration; if (!InspIRCd::Duration(parameters[1], duration)) { user->WriteNotice("Invalid ban time"); return CMD_FAILURE; } unsigned long expire = duration + ServerInstance->Time(); std::string mask = parameters[2]; bool isextban = ((mask.size() > 2) && (mask[1] == ':')); if (!isextban && !InspIRCd::IsValidMask(mask)) mask.append("!*@*"); if (IsBanSet(channel, mask)) { user->WriteNotice("Ban already set"); return CMD_FAILURE; } Modes::ChangeList setban; setban.push_add(ServerInstance->Modes.FindMode('b', MODETYPE_CHANNEL), mask); // Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to // make it so that the user sets the mode themselves ServerInstance->Modes.Process(user, channel, NULL, setban); if (ServerInstance->Modes.GetLastChangeList().empty()) { user->WriteNotice("Invalid ban mask"); return CMD_FAILURE; } T.mask = mask; T.expire = expire + (IS_REMOTE(user) ? 5 : 0); T.chan = channel; TimedBanList.push_back(T); const std::string addban = user->nick + " added a timed ban on " + mask + " lasting for " + InspIRCd::DurationString(duration) + "."; // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above PrefixMode* mh = ServerInstance->Modes.FindPrefixMode('h'); char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@'; ClientProtocol::Messages::Privmsg notice(ServerInstance->FakeClient, channel, addban, MSG_NOTICE); channel->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar); ServerInstance->PI->SendChannelNotice(channel, pfxchar, addban); return CMD_SUCCESS; }
void OnBackgroundTimer(time_t curtime) override { timedbans expired; for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();) { if (curtime > i->expire) { expired.push_back(*i); i = TimedBanList.erase(i); } else ++i; } for (timedbans::iterator i = expired.begin(); i != expired.end(); i++) { std::string mask = i->mask; Channel* cr = i->chan; { const std::string expiry = "*** Timed ban on " + cr->name + " expired."; // If halfop is loaded, send notice to halfops and above, otherwise send to ops and above PrefixMode* mh = ServerInstance->Modes.FindPrefixMode('h'); char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@'; ClientProtocol::Messages::Privmsg notice(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, cr, expiry, MSG_NOTICE); cr->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar); ServerInstance->PI->SendChannelNotice(cr, pfxchar, expiry); Modes::ChangeList setban; setban.push_remove(ServerInstance->Modes.FindMode('b', MODETYPE_CHANNEL), mask); ServerInstance->Modes.Process(ServerInstance->FakeClient, cr, NULL, setban); } } }
PrefixMode* ModeParser::FindPrefix(unsigned const char pfxletter) { const PrefixModeList& list = GetPrefixModes(); for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i) { PrefixMode* pm = *i; if (pm->GetPrefix() == pfxletter) return pm; } return NULL; }
void ModeParser::AddMode(ModeHandler* mh) { /* Yes, i know, this might let people declare modes like '_' or '^'. * If they do that, thats their problem, and if i ever EVER see an * official InspIRCd developer do that, i'll beat them with a paddle! */ if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z')) throw ModuleException("Invalid letter for mode " + mh->name); /* A mode prefix of ',' is not acceptable, it would f**k up server to server. * A mode prefix of ':' will f**k up both server to server, and client to server. * A mode prefix of '#' will mess up /whois and /privmsg */ PrefixMode* pm = mh->IsPrefixMode(); if (pm) { if ((pm->GetPrefix() > 126) || (pm->GetPrefix() == ',') || (pm->GetPrefix() == ':') || (pm->GetPrefix() == '#')) throw ModuleException("Invalid prefix for mode " + mh->name); if (FindPrefix(pm->GetPrefix())) throw ModuleException("Prefix already exists for mode " + mh->name); } ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65]; if (slot) throw ModuleException("Letter is already in use for mode " + mh->name); // The mode needs an id if it is either a user mode, a simple mode (flag) or a parameter mode. // Otherwise (for listmodes and prefix modes) the id remains MODEID_MAX, which is invalid. ModeHandler::Id modeid = MODEID_MAX; if ((mh->GetModeType() == MODETYPE_USER) || (mh->IsParameterMode()) || (!mh->IsListMode())) modeid = AllocateModeId(mh->GetModeType()); if (!modehandlersbyname[mh->GetModeType()].insert(std::make_pair(mh->name, mh)).second) throw ModuleException("Mode name already in use: " + mh->name); // Everything is fine, add the mode // If we allocated an id for this mode then save it and put the mode handler into the slot if (modeid != MODEID_MAX) { mh->modeid = modeid; modehandlersbyid[mh->GetModeType()][modeid] = mh; } slot = mh; if (pm) mhlist.prefix.push_back(pm); else if (mh->IsListModeBase()) mhlist.list.push_back(mh->IsListModeBase()); RecreateModeListFor004Numeric(); }
std::string ModeParser::GiveModeList(ModeMasks m) { 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++) { unsigned char pos = (mode-65) | m; /* One parameter when adding */ if (modehandlers[pos]) { if (modehandlers[pos]->GetNumParams(true)) { PrefixMode* pm = modehandlers[pos]->IsPrefixMode(); if ((modehandlers[pos]->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0))) { type1 += modehandlers[pos]->GetModeChar(); } else { /* ... and one parameter when removing */ if (modehandlers[pos]->GetNumParams(false)) { /* But not a list mode */ if (!pm) { type2 += modehandlers[pos]->GetModeChar(); } } else { /* No parameters when removing */ type3 += modehandlers[pos]->GetModeChar(); } } } else { type4 += modehandlers[pos]->GetModeChar(); } } } return type1 + "," + type2 + "," + type3 + "," + type4; }
void init() override { ConfigTagList tags = ServerInstance->Config->ConfTags("customprefix"); for (ConfigIter iter = tags.first; iter != tags.second; ++iter) { ConfigTag* tag = iter->second; const std::string name = tag->getString("name"); if (name.empty()) throw ModuleException("<customprefix:name> must be specified at " + tag->getTagLocation()); if (tag->getBool("change")) { ModeHandler* mh = ServerInstance->Modes.FindMode(name, MODETYPE_CHANNEL); if (!mh) throw ModuleException("<customprefix:change> specified for a non-existent mode at " + tag->getTagLocation()); PrefixMode* pm = mh->IsPrefixMode(); if (!pm) throw ModuleException("<customprefix:change> specified for a non-prefix mode at " + tag->getTagLocation()); unsigned long rank = tag->getUInt("rank", pm->GetPrefixRank(), 0, UINT_MAX); unsigned long setrank = tag->getUInt("ranktoset", pm->GetLevelRequired(true), rank, UINT_MAX); unsigned long unsetrank = tag->getUInt("ranktounset", pm->GetLevelRequired(false), setrank, UINT_MAX); bool depriv = tag->getBool("depriv", pm->CanSelfRemove()); pm->Update(rank, setrank, unsetrank, depriv); ServerInstance->Logs.Log(MODNAME, LOG_DEBUG, "Changed the %s prefix: depriv=%u rank=%u ranktoset=%u ranktounset=%u", pm->name.c_str(), pm->CanSelfRemove(), pm->GetPrefixRank(), pm->GetLevelRequired(true), pm->GetLevelRequired(false)); continue; } const std::string letter = tag->getString("letter"); if (letter.length() != 1) throw ModuleException("<customprefix:letter> must be set to a mode character at " + tag->getTagLocation()); const std::string prefix = tag->getString("prefix"); if (prefix.length() != 1) throw ModuleException("<customprefix:prefix> must be set to a mode prefix at " + tag->getTagLocation()); try { CustomPrefixMode* mh = new CustomPrefixMode(this, name, letter[0], prefix[0], tag); modes.push_back(mh); ServerInstance->Modules.AddService(*mh); } catch (ModuleException& e) { throw ModuleException(e.GetReason() + " (while creating mode from " + tag->getTagLocation() + ")"); } } }
bool ModeParser::AddMode(ModeHandler* mh) { unsigned char mask = 0; unsigned char pos = 0; /* Yes, i know, this might let people declare modes like '_' or '^'. * If they do that, thats their problem, and if i ever EVER see an * official InspIRCd developer do that, i'll beat them with a paddle! */ if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z')) return false; /* A mode prefix of ',' is not acceptable, it would f**k up server to server. * A mode prefix of ':' will f**k up both server to server, and client to server. * A mode prefix of '#' will mess up /whois and /privmsg */ PrefixMode* pm = mh->IsPrefixMode(); if (pm) { if ((pm->GetPrefix() > 126) || (pm->GetPrefix() == ',') || (pm->GetPrefix() == ':') || (pm->GetPrefix() == '#')) return false; if (FindPrefix(pm->GetPrefix())) return false; } mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL; pos = (mh->GetModeChar()-65) | mask; if (modehandlers[pos]) return false; // Everything is fine, add the mode modehandlers[pos] = mh; if (pm) mhlist.prefix.push_back(pm); else if (mh->IsListModeBase()) mhlist.list.push_back(mh->IsListModeBase()); RecreateModeListFor004Numeric(); return true; }
std::string ModeParser::BuildPrefixes(bool lettersAndModes) { std::string mletters; std::string mprefixes; std::map<int,std::pair<char,char> > prefixes; const PrefixModeList& list = GetPrefixModes(); for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i) { PrefixMode* pm = *i; if (pm->GetPrefix()) prefixes[pm->GetPrefixRank()] = std::make_pair(pm->GetPrefix(), pm->GetModeChar()); } for(std::map<int,std::pair<char,char> >::reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); n++) { mletters = mletters + n->second.first; mprefixes = mprefixes + n->second.second; } return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters; }
ModResult OnCheckExemptionHandler::Call(User* user, Channel* chan, const std::string& restriction) { unsigned int mypfx = chan->GetPrefixValue(user); char minmode = 0; std::string current; irc::spacesepstream defaultstream(ServerInstance->Config->ConfValue("options")->getString("exemptchanops")); while (defaultstream.GetToken(current)) { std::string::size_type pos = current.find(':'); if (pos == std::string::npos) continue; if (!current.compare(0, pos, restriction)) minmode = current[pos+1]; } PrefixMode* mh = ServerInstance->Modes->FindPrefixMode(minmode); if (mh && mypfx >= mh->GetPrefixRank()) return MOD_RES_ALLOW; if (mh || minmode == '*') return MOD_RES_DENY; return MOD_RES_PASSTHRU; }
CmdResult Handle(User* user, const Params& parameters) override { 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(false)) { user->WriteNotice("You do not have access to unset " + ConvToStr(modeletter) + " on " + chan->name + "."); return CMD_FAILURE; } std::string pattern = parameters.size() > 2 ? parameters[2] : "*"; PrefixMode* pm; ListModeBase* lm; ListModeBase::ModeList* ml; Modes::ChangeList changelist; if ((pm = mh->IsPrefixMode())) { // As user prefix modes don't have a GetList() method, let's iterate through the channel's users. const Channel::MemberMap& users = chan->GetUsers(); for (Channel::MemberMap::const_iterator it = users.begin(); it != users.end(); ++it) { if (!InspIRCd::Match(it->first->nick, pattern)) continue; if (it->second->HasMode(pm) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE))) changelist.push_remove(mh, it->first->nick); } } else if ((lm = mh->IsListModeBase()) && ((ml = lm->GetList(chan)) != NULL)) { for (ListModeBase::ModeList::iterator it = ml->begin(); it != ml->end(); ++it) { if (!InspIRCd::Match(it->mask, pattern)) continue; changelist.push_remove(mh, it->mask); } } else { if (chan->IsModeSet(mh)) changelist.push_remove(mh); } ServerInstance->Modes.Process(user, chan, NULL, changelist); return CMD_SUCCESS; }
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; }
/** Handle /INVITE */ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user) { ModResult MOD_RESULT; if (parameters.size() >= 2) { User* u; if (IS_LOCAL(user)) u = ServerInstance->FindNickOnly(parameters[0]); else u = ServerInstance->FindNick(parameters[0]); Channel* c = ServerInstance->FindChan(parameters[1]); time_t timeout = 0; if (parameters.size() >= 3) { if (IS_LOCAL(user)) timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[2]); else if (parameters.size() > 3) timeout = ConvToInt(parameters[3]); } if ((!c) || (!u) || (u->registered != REG_ALL)) { user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c ? parameters[0].c_str() : parameters[1].c_str()); return CMD_FAILURE; } // Verify channel timestamp if the INVITE is coming from a remote server if (!IS_LOCAL(user)) { // Remote INVITE commands must carry a channel timestamp if (parameters.size() < 3) return CMD_INVALID; // Drop the invite if our channel TS is lower time_t RemoteTS = ConvToInt(parameters[2]); if (c->age < RemoteTS) return CMD_FAILURE; } if ((IS_LOCAL(user)) && (!c->HasUser(user))) { user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", c->name.c_str()); return CMD_FAILURE; } if (c->HasUser(u)) { user->WriteNumeric(ERR_USERONCHANNEL, "%s %s :is already on channel", u->nick.c_str(), c->name.c_str()); return CMD_FAILURE; } FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout)); if (MOD_RESULT == MOD_RES_DENY) { return CMD_FAILURE; } else if (MOD_RESULT == MOD_RES_PASSTHRU) { if (IS_LOCAL(user)) { unsigned int rank = c->GetPrefixValue(user); if (rank < HALFOP_VALUE) { // Check whether halfop mode is available and phrase error message accordingly ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL); user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You must be a channel %soperator", c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : "")); return CMD_FAILURE; } } } if (IS_LOCAL(u)) { Invitation::Create(c, IS_LOCAL(u), timeout); u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str()); } if (IS_LOCAL(user)) user->WriteNumeric(RPL_INVITING, "%s %s", u->nick.c_str(),c->name.c_str()); if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE) { char prefix; switch (ServerInstance->Config->AnnounceInvites) { case ServerConfig::INVITE_ANNOUNCE_OPS: { prefix = '@'; break; } case ServerConfig::INVITE_ANNOUNCE_DYNAMIC: { PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h'); prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@'); break; } default: { prefix = 0; break; } } c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str()); } FOREACH_MOD(OnUserInvite, (user,u,c,timeout)); } else if (IS_LOCAL(user)) { // pinched from ircu - invite with not enough parameters shows channels // youve been invited to but haven't joined yet. InviteList& il = IS_LOCAL(user)->GetInviteList(); for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i) { user->WriteNumeric(RPL_INVITELIST, ":%s", (*i)->chan->name.c_str()); } user->WriteNumeric(RPL_ENDOFINVITELIST, ":End of INVITE list"); } return CMD_SUCCESS; }
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; } std::string pattern = parameters.size() > 2 ? parameters[2] : "*"; PrefixMode* pm; ListModeBase* lm; ListModeBase::ModeList* ml; irc::modestacker modestack(false); if ((pm = mh->IsPrefixMode())) { // As user prefix modes don't have a GetList() method, let's iterate through the channel's users. const Channel::MemberMap& users = chan->GetUsers(); for (Channel::MemberMap::const_iterator it = users.begin(); it != users.end(); ++it) { if (!InspIRCd::Match(it->first->nick, pattern)) continue; if (it->second->hasMode(modeletter) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE))) modestack.Push(modeletter, it->first->nick); } } else if ((lm = mh->IsListModeBase()) && ((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 { if (chan->IsModeSet(mh)) modestack.Push(modeletter); } 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; }