void Execute(IRCServer *server, User *u, const std::vector<std::string> ¶ms) { const std::string &target = params.size() > 0 ? params[0] : ""; bool operonly = params.size() > 1 && params[1] == "o"; if (operonly) ; else if (target.empty()) { for (std::map<std::string, User *, Sinkhole::less_ci>::iterator it = server->Users.begin(), it_end = server->Users.end(); it != it_end; ++it) { User *user = it->second; if (this->CanShow(u, user)) this->WriteWho(server, u, user, NULL); } } else if (target[0] == '#' || target[0] == '&') { Channel *c = server->FindChannel(target); if (c != NULL) { const std::set<User *> &users = c->GetUsers(); for (std::set<User *>::const_iterator it = users.begin(), it_end = users.end(); it != it_end; ++it) { User *user = *it; if (this->CanShow(u, user)) this->WriteWho(server, u, user, c); } } } else { User *user = server->FindUser(target); if (user != NULL) { if (this->CanShow(u, user)) this->WriteWho(server, u, user, NULL); } else for (std::map<std::string, User *, Sinkhole::less_ci>::iterator it = server->Users.begin(), it_end = server->Users.end(); it != it_end; ++it) { user = it->second; if (Sinkhole::match(user->GetNick(), target)) if (this->CanShow(u, user)) this->WriteWho(server, u, user, NULL); } } u->WriteNumeric(315, (!target.empty() ? target : "*") + " :End of /WHO list."); }
void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self) { // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user // and visit all users on those channels. Because two users may share more than one common channel, // we must skip users that we have already visited. // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser. // The global counter is incremented every time we do something for each neighbor of a user. Then, // before visiting a member we examine user->already_sent. If it's equal to the current counter, we // skip the member. Otherwise, we set it to the current counter and visit the member. // Ask modules to build a list of exceptions. // Mods may also exclude entire channels by erasing them from include_chans. IncludeChanList include_chans(chans.begin(), chans.end()); std::map<User*, bool> exceptions; exceptions[this] = include_self; FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions)); // Get next id, guaranteed to differ from the already_sent field of all users const already_sent_t newid = ++LocalUser::already_sent_id; // Handle exceptions first for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i) { LocalUser* curr = IS_LOCAL(i->first); if (curr) { // Mark as visited to ensure we won't visit again if there is a common channel curr->already_sent = newid; // Always treat quitting users as excluded if ((i->second) && (!curr->quitting)) handler.Execute(curr); } } // Now consider the real neighbors for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i) { Channel* chan = (*i)->chan; const Channel::MemberMap& userlist = chan->GetUsers(); for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j) { LocalUser* curr = IS_LOCAL(j->first); // User not yet visited? if ((curr) && (curr->already_sent != newid)) { // Mark as visited and execute function curr->already_sent = newid; handler.Execute(curr); } } } }
void User::WriteCommonRaw(const std::string &line, bool include_self) { if (this->registered != REG_ALL || quitting) return; LocalUser::already_sent_id++; IncludeChanList include_c(chans.begin(), chans.end()); std::map<User*,bool> exceptions; exceptions[this] = include_self; FOREACH_MOD(OnBuildNeighborList, (this, include_c, exceptions)); for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i) { LocalUser* u = IS_LOCAL(i->first); if (u && !u->quitting) { u->already_sent = LocalUser::already_sent_id; if (i->second) u->Write(line); } } for (IncludeChanList::const_iterator v = include_c.begin(); v != include_c.end(); ++v) { Channel* c = (*v)->chan; const UserMembList* ulist = c->GetUsers(); for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++) { LocalUser* u = IS_LOCAL(i->first); if (u && u->already_sent != LocalUser::already_sent_id) { u->already_sent = LocalUser::already_sent_id; u->Write(line); } } } }
void ValidateChans() { badchan = true; std::vector<Channel*> chanvec; for (chan_hash::const_iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); ++i) { if (!ServerInstance->IsChannel(i->second->name)) chanvec.push_back(i->second); } std::vector<Channel*>::reverse_iterator c2 = chanvec.rbegin(); while (c2 != chanvec.rend()) { Channel* c = *c2++; if (c->IsModeSet(permchannelmode) && c->GetUserCounter()) { std::vector<std::string> modes; modes.push_back(c->name); modes.push_back(std::string("-") + permchannelmode->GetModeChar()); ServerInstance->Modes->Process(modes, ServerInstance->FakeClient); } const UserMembList* users = c->GetUsers(); for(UserMembCIter j = users->begin(); j != users->end(); ) { if (IS_LOCAL(j->first)) { // KickUser invalidates the iterator UserMembCIter it = j++; c->KickUser(ServerInstance->FakeClient, it->first, "Channel name no longer valid"); } else ++j; } } badchan = false; }
CmdResult Handle (const std::vector<std::string> ¶meters, User *user) { if (parameters.size() > 1 && parameters[1] != ServerInstance->Config->ServerName.c_str()) return CMD_SUCCESS; User *targuser; Channel *targchan; std::string checkstr; std::string chliststr; checkstr = ":" + ServerInstance->Config->ServerName + " 304 " + user->nick + " :CHECK"; targuser = ServerInstance->FindNick(parameters[0]); targchan = ServerInstance->FindChan(parameters[0]); /* * Syntax of a /check reply: * :server.name 304 target :CHECK START <target> * :server.name 304 target :CHECK <field> <value> * :server.name 304 target :CHECK END */ user->SendText(checkstr + " START " + parameters[0]); if (targuser) { LocalUser* loctarg = IS_LOCAL(targuser); /* /check on a user */ user->SendText(checkstr + " nuh " + targuser->GetFullHost()); user->SendText(checkstr + " realnuh " + targuser->GetFullRealHost()); user->SendText(checkstr + " realname " + targuser->fullname); user->SendText(checkstr + " modes +" + targuser->FormatModes()); user->SendText(checkstr + " snomasks " + GetSnomasks(targuser)); user->SendText(checkstr + " server " + targuser->server->GetName()); user->SendText(checkstr + " uid " + targuser->uuid); user->SendText(checkstr + " signon " + timestring(targuser->signon)); user->SendText(checkstr + " nickts " + timestring(targuser->age)); if (loctarg) user->SendText(checkstr + " lastmsg " + timestring(loctarg->idle_lastmsg)); if (targuser->IsAway()) { /* user is away */ user->SendText(checkstr + " awaytime " + timestring(targuser->awaytime)); user->SendText(checkstr + " awaymsg " + targuser->awaymsg); } if (targuser->IsOper()) { OperInfo* oper = targuser->oper; /* user is an oper of type ____ */ user->SendText(checkstr + " opertype " + oper->name); if (loctarg) { std::string umodes; std::string cmodes; for(char c='A'; c < 'z'; c++) { ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER); if (mh && mh->NeedsOper() && loctarg->HasModePermission(c, MODETYPE_USER)) umodes.push_back(c); mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL); if (mh && mh->NeedsOper() && loctarg->HasModePermission(c, MODETYPE_CHANNEL)) cmodes.push_back(c); } user->SendText(checkstr + " modeperms user="******" channel=" + cmodes); std::string opcmds; for(std::set<std::string>::iterator i = oper->AllowedOperCommands.begin(); i != oper->AllowedOperCommands.end(); i++) { opcmds.push_back(' '); opcmds.append(*i); } std::stringstream opcmddump(opcmds); user->SendText(checkstr + " commandperms", opcmddump); std::string privs; for(std::set<std::string>::iterator i = oper->AllowedPrivs.begin(); i != oper->AllowedPrivs.end(); i++) { privs.push_back(' '); privs.append(*i); } std::stringstream privdump(privs); user->SendText(checkstr + " permissions", privdump); } } if (loctarg) { user->SendText(checkstr + " clientaddr " + loctarg->client_sa.str()); user->SendText(checkstr + " serveraddr " + loctarg->server_sa.str()); std::string classname = loctarg->GetClass()->name; if (!classname.empty()) user->SendText(checkstr + " connectclass " + classname); } else user->SendText(checkstr + " onip " + targuser->GetIPString()); for (UCListIter i = targuser->chans.begin(); i != targuser->chans.end(); i++) { Channel* c = (*i)->chan; chliststr.append(c->GetPrefixChar(targuser)).append(c->name).append(" "); } std::stringstream dump(chliststr); user->SendText(checkstr + " onchans", dump); dumpExt(user, checkstr, targuser); } else if (targchan) { /* /check on a channel */ user->SendText(checkstr + " timestamp " + timestring(targchan->age)); if (targchan->topic[0] != 0) { /* there is a topic, assume topic related information exists */ user->SendText(checkstr + " topic " + targchan->topic); user->SendText(checkstr + " topic_setby " + targchan->setby); user->SendText(checkstr + " topic_setat " + timestring(targchan->topicset)); } user->SendText(checkstr + " modes " + targchan->ChanModes(true)); user->SendText(checkstr + " membercount " + ConvToStr(targchan->GetUserCounter())); /* now the ugly bit, spool current members of a channel. :| */ const UserMembList *ulist= targchan->GetUsers(); /* note that unlike /names, we do NOT check +i vs in the channel */ for (UserMembCIter i = ulist->begin(); i != ulist->end(); i++) { /* * Unlike Asuka, I define a clone as coming from the same host. --w00t */ user->SendText("%s member %-3lu %s%s (%s@%s) %s ", checkstr.c_str(), ServerInstance->Users->GlobalCloneCount(i->first), targchan->GetAllPrefixChars(i->first), i->first->nick.c_str(), i->first->ident.c_str(), i->first->dhost.c_str(), i->first->fullname.c_str()); } const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes(); for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i) dumpListMode(user, checkstr, (*i)->GetList(targchan)); dumpExt(user, checkstr, targchan); } else { /* /check on an IP address, or something that doesn't exist */ long x = 0; /* hostname or other */ for (user_hash::const_iterator a = ServerInstance->Users->clientlist->begin(); a != ServerInstance->Users->clientlist->end(); a++) { if (InspIRCd::Match(a->second->host, parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->dhost, parameters[0], ascii_case_insensitive_map)) { /* host or vhost matches mask */ user->SendText(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname); } /* IP address */ else if (InspIRCd::MatchCIDR(a->second->GetIPString(), parameters[0])) { /* same IP. */ user->SendText(checkstr + " match " + ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->fullname); } } user->SendText(checkstr + " matches " + ConvToStr(x)); } user->SendText(checkstr + " END " + parameters[0]); return CMD_SUCCESS; }
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; }
void ModuleManager::DoSafeUnload(Module* mod) { // First, notify all modules that a module is about to be unloaded, so in case // they pass execution to the soon to be unloaded module, it will happen now, // i.e. before we unregister the services of the module being unloaded FOREACH_MOD(OnUnloadModule, (mod)); std::map<std::string, Module*>::iterator modfind = Modules.find(mod->ModuleSourceFile); std::vector<reference<ExtensionItem> > items; ServerInstance->Extensions.BeginUnregister(modfind->second, items); /* Give the module a chance to tidy out all its metadata */ const chan_hash& chans = ServerInstance->GetChans(); for (chan_hash::const_iterator c = chans.begin(); c != chans.end(); ) { Channel* chan = c->second; ++c; mod->OnCleanup(TYPE_CHANNEL, chan); chan->doUnhookExtensions(items); const Channel::MemberMap& users = chan->GetUsers(); for (Channel::MemberMap::const_iterator mi = users.begin(); mi != users.end(); ++mi) mi->second->doUnhookExtensions(items); } const user_hash& users = ServerInstance->Users->GetUsers(); for (user_hash::const_iterator u = users.begin(); u != users.end(); ) { User* user = u->second; // The module may quit the user (e.g. SSL mod unloading) and that will remove it from the container ++u; mod->OnCleanup(TYPE_USER, user); user->doUnhookExtensions(items); } const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER); for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); ) { ModeHandler* mh = i->second; ++i; if (mh->creator == mod) this->DelService(*mh); } const ModeParser::ModeHandlerMap& chanmodes = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL); for (ModeParser::ModeHandlerMap::const_iterator i = chanmodes.begin(); i != chanmodes.end(); ) { ModeHandler* mh = i->second; ++i; if (mh->creator == mod) this->DelService(*mh); } for(std::multimap<std::string, ServiceProvider*>::iterator i = DataProviders.begin(); i != DataProviders.end(); ) { std::multimap<std::string, ServiceProvider*>::iterator curr = i++; if (curr->second->creator == mod) DataProviders.erase(curr); } dynamic_reference_base::reset_all(); DetachAll(mod); Modules.erase(modfind); ServerInstance->GlobalCulls.AddItem(mod); ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Module %s unloaded",mod->ModuleSourceFile.c_str()); ServerInstance->ISupport.Build(); }
void User::DoHostCycle(const std::string &quitline) { char buffer[MAXBUF]; if (!ServerInstance->Config->CycleHosts) return; already_sent_t silent_id = ++LocalUser::already_sent_id; already_sent_t seen_id = ++LocalUser::already_sent_id; UserChanList include_c(chans); std::map<User*,bool> exceptions; FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions)); for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i) { LocalUser* u = IS_LOCAL(i->first); if (u && !u->quitting) { if (i->second) { u->already_sent = seen_id; u->Write(quitline); } else { u->already_sent = silent_id; } } } for (UCListIter v = include_c.begin(); v != include_c.end(); ++v) { Channel* c = *v; snprintf(buffer, MAXBUF, ":%s JOIN %s", GetFullHost().c_str(), c->name.c_str()); std::string joinline(buffer); Membership* memb = c->GetUser(this); std::string modeline = memb->modes; if (modeline.length() > 0) { for(unsigned int i=0; i < memb->modes.length(); i++) modeline.append(" ").append(nick); snprintf(buffer, MAXBUF, ":%s MODE %s +%s", ServerInstance->Config->CycleHostsFromUser ? GetFullHost().c_str() : ServerInstance->Config->ServerName.c_str(), c->name.c_str(), modeline.c_str()); modeline = buffer; } const UserMembList *ulist = c->GetUsers(); for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++) { LocalUser* u = IS_LOCAL(i->first); if (u == NULL || u == this) continue; if (u->already_sent == silent_id) continue; if (u->already_sent != seen_id) { u->Write(quitline); u->already_sent = seen_id; } u->Write(joinline); if (modeline.length() > 0) u->Write(modeline); } } }
CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user) { /* * XXX - RFC says: * The <name> passed to WHO is matched against users' host, server, real * name and nickname * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC. */ /* WHO options */ opt_viewopersonly = false; opt_showrealhost = false; opt_realname = false; opt_mode = false; opt_ident = false; opt_metadata = false; opt_port = false; opt_away = false; opt_local = false; opt_far = false; opt_time = false; std::vector<std::string> whoresults; std::string initial = "352 " + user->nick + " "; /* Change '0' into '*' so the wildcard matcher can grok it */ std::string matchtext = ((parameters[0] == "0") ? "*" : parameters[0]); // WHO flags count as a wildcard bool usingwildcards = ((parameters.size() > 1) || (matchtext.find_first_of("*?.") != std::string::npos)); if (parameters.size() > 1) { for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter) { switch (*iter) { case 'o': opt_viewopersonly = true; break; case 'h': if (user->HasPrivPermission("users/auspex")) opt_showrealhost = true; break; case 'r': opt_realname = true; break; case 'm': if (user->HasPrivPermission("users/auspex")) opt_mode = true; break; case 'M': if (user->HasPrivPermission("users/auspex")) opt_metadata = true; break; case 'i': opt_ident = true; break; case 'p': if (user->HasPrivPermission("users/auspex")) opt_port = true; break; case 'a': opt_away = true; break; case 'l': if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty()) opt_local = true; break; case 'f': if (user->HasPrivPermission("users/auspex") || ServerInstance->Config->HideWhoisServer.empty()) opt_far = true; break; case 't': opt_time = true; break; } } } /* who on a channel? */ Channel* ch = ServerInstance->FindChan(matchtext); if (ch) { if (CanView(ch,user)) { bool inside = ch->HasUser(user); /* who on a channel. */ const UserMembList *cu = ch->GetUsers(); for (UserMembCIter i = cu->begin(); i != cu->end(); i++) { /* None of this applies if we WHO ourselves */ if (user != i->first) { /* opers only, please */ if (opt_viewopersonly && !i->first->IsOper()) continue; /* If we're not inside the channel, hide +i users */ if (i->first->IsModeSet(invisiblemode) && !inside && !user->HasPrivPermission("users/auspex")) continue; } SendWhoLine(user, parameters, initial, ch, i->first, whoresults); } } } else { /* Match against wildcard of nick, server or host */ if (opt_viewopersonly) { /* Showing only opers */ for (std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++) { User* oper = *i; if (whomatch(user, oper, matchtext.c_str())) { if (!user->SharesChannelWith(oper)) { if (usingwildcards && (!oper->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex"))) continue; } SendWhoLine(user, parameters, initial, NULL, oper, whoresults); } } } else { for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++) { if (whomatch(user, i->second, matchtext.c_str())) { if (!user->SharesChannelWith(i->second)) { if (usingwildcards && (i->second->IsModeSet(invisiblemode)) && (!user->HasPrivPermission("users/auspex"))) continue; } SendWhoLine(user, parameters, initial, NULL, i->second, whoresults); } } } } /* Send the results out */ for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++) user->WriteServ(*n); user->WriteNumeric(RPL_ENDOFWHO, "%s :End of /WHO list.", *parameters[0].c_str() ? parameters[0].c_str() : "*"); // Penalize the user a bit for large queries // (add one unit of penalty per 200 results) if (IS_LOCAL(user)) IS_LOCAL(user)->CommandFloodPenalty += whoresults.size() * 5; return CMD_SUCCESS; }
CmdResult HandleChannelTarget(User* source, const Params& parameters, const char* target, PrefixMode* pm) { Channel* chan = ServerInstance->FindChan(target); if (!chan) { // The target channel does not exist. source->WriteNumeric(Numerics::NoSuchChannel(parameters[0])); return CMD_FAILURE; } if (IS_LOCAL(source)) { if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(source)) { // The noextmsg mode is set and the source is not in the channel. source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)"); return CMD_FAILURE; } bool no_chan_priv = chan->GetPrefixValue(source) < VOICE_VALUE; if (no_chan_priv && chan->IsModeSet(moderatedmode)) { // The moderated mode is set and the source has no status rank. source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m)"); return CMD_FAILURE; } if (no_chan_priv && ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL && chan->IsBanned(source)) { // The source is banned in the channel and restrictbannedusers is enabled. if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY) source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)"); return CMD_FAILURE; } } // Fire the pre-message events. MessageTarget msgtarget(chan, pm ? pm->GetPrefix() : 0); CTCTags::TagMessageDetails msgdetails(parameters.GetTags()); if (!FirePreEvents(source, msgtarget, msgdetails)) return CMD_FAILURE; unsigned int minrank = pm ? pm->GetPrefixRank() : 0; CTCTags::TagMessage message(source, chan, parameters.GetTags()); const Channel::MemberMap& userlist = chan->GetUsers(); for (Channel::MemberMap::const_iterator iter = userlist.begin(); iter != userlist.end(); ++iter) { LocalUser* luser = IS_LOCAL(iter->first); // Don't send to remote users or the user who is the source. if (!luser || luser == source) continue; // Don't send to unprivileged or exempt users. if (iter->second->getRank() < minrank || msgdetails.exemptions.count(luser)) continue; // Send to users if they have the capability. if (cap.get(luser)) luser->Send(msgevprov, message); } return FirePostEvent(source, msgtarget, msgdetails); }
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; }