void RemoveMode(Channel* channel, irc::modestacker* stack) { const UserMembList* cl = channel->GetUsers(); std::vector<std::string> mode_junk; mode_junk.push_back(channel->name); irc::modestacker modestack(false); std::vector<std::string> stackresult; for (UserMembCIter i = cl->begin(); i != cl->end(); i++) { if (i->second->hasMode('Y')) { if (stack) stack->Push(this->GetModeChar(), i->first->nick); else modestack.Push(this->GetModeChar(), i->first->nick); } } if (stack) return; while (modestack.GetStackedLine(stackresult)) { mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end()); ServerInstance->SendMode(mode_junk, ServerInstance->FakeClient); mode_junk.erase(mode_junk.begin() + 1, mode_junk.end()); } }
void RemoveMode(Channel* channel, char mc, irc::modestacker* stack) { CUList* cl = channel->GetUsers(); std::string item = extend + std::string(channel->name); std::vector<std::string> mode_junk; mode_junk.push_back(channel->name); irc::modestacker modestack(MyInstance, false); std::deque<std::string> stackresult; for (CUList::iterator i = cl->begin(); i != cl->end(); i++) { if (i->first->GetExt(item)) { if (stack) stack->Push(mc, i->first->nick); else modestack.Push(mc, i->first->nick); } } if (stack) return; while (modestack.GetStackedLine(stackresult)) { mode_junk.insert(mode_junk.end(), stackresult.begin(), stackresult.end()); MyInstance->SendMode(mode_junk, MyInstance->FakeClient); mode_junk.erase(mode_junk.begin() + 1, mode_junk.end()); } }
void RemoveMode(chanrec* channel, char mc) { unload_kludge = true; CUList* cl = channel->GetUsers(); std::string item = extend + std::string(channel->name); const char* mode_junk[MAXMODES+2]; userrec* n = new userrec(MyInstance); n->SetFd(FD_MAGIC_NUMBER); mode_junk[0] = channel->name; irc::modestacker modestack(false); std::deque<std::string> stackresult; for (CUList::iterator i = cl->begin(); i != cl->end(); i++) { if (i->first->GetExt(item, dummyptr)) { modestack.Push(mc, i->first->nick); } } while (modestack.GetStackedLine(stackresult)) { for (size_t j = 0; j < stackresult.size(); j++) { mode_junk[j+1] = stackresult[j].c_str(); } MyInstance->SendMode(mode_junk, stackresult.size() + 1, n); } delete n; unload_kludge = false; }
virtual void OnSyncChannel(chanrec* chan, Module* proto, void* opaque) { /* NOTE: If +qa prefix is on, this is propogated by the channel join, * so we dont need to propogate it manually */ if (!QAPrefixes) { // this is called when the server is linking into a net and wants to sync channel data. // we should send our mode changes for the channel here to ensure that other servers // know whos +q/+a on the channel. CUList* cl = chan->GetUsers(); string_list commands; std::string founder = "cm_founder_"+std::string(chan->name); std::string protect = "cm_protect_"+std::string(chan->name); irc::modestacker modestack(true); std::deque<std::string> stackresult; for (CUList::iterator i = cl->begin(); i != cl->end(); i++) { if (i->first->GetExt(founder,dummyptr)) { modestack.Push('q',i->first->nick); } if (i->first->GetExt(protect,dummyptr)) { modestack.Push('a',i->first->nick); } } while (modestack.GetStackedLine(stackresult)) { irc::stringjoiner mode_join(" ", stackresult, 0, stackresult.size() - 1); std::string line = mode_join.GetJoined(); proto->ProtoSendMode(opaque,TYPE_CHANNEL,chan, line); } } }
void ListModeBase::DoSyncChannel(Channel* chan, Module* proto, void* opaque) { ChanData* cd = extItem.get(chan); if (!cd) return; irc::modestacker modestack(true); std::vector<std::string> stackresult; std::vector<TranslateType> types; types.push_back(TR_TEXT); for (ModeList::iterator it = cd->list.begin(); it != cd->list.end(); it++) modestack.Push(mode, it->mask); while (modestack.GetStackedLine(stackresult)) { types.assign(stackresult.size(), this->GetTranslateType()); proto->ProtoSendMode(opaque, TYPE_CHANNEL, chan, stackresult, types); stackresult.clear(); } }
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */ CmdResult CommandFJoin::Handle(const std::vector<std::string>& params, User *srcuser) { SpanningTreeUtilities* Utils = ((ModuleSpanningTree*)(Module*)creator)->Utils; /* 1.1+ FJOIN works as follows: * * Each FJOIN is sent along with a timestamp, and the side with the lowest * timestamp 'wins'. From this point on we will refer to this side as the * winner. The side with the higher timestamp loses, from this point on we * will call this side the loser or losing side. This should be familiar to * anyone who's dealt with dreamforge or TS6 before. * * When two sides of a split heal and this occurs, the following things * will happen: * * If the timestamps are exactly equal, both sides merge their privilages * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been * re-created during a split, this is safe to do. * * If the timestamps are NOT equal, the losing side removes all of its * modes from the channel, before introducing new users into the channel * which are listed in the FJOIN command's parameters. The losing side then * LOWERS its timestamp value of the channel to match that of the winning * side, and the modes of the users of the winning side are merged in with * the losing side. * * The winning side on the other hand will ignore all user modes from the * losing side, so only its own modes get applied. Life is simple for those * who succeed at internets. :-) * * Syntax: * :<sid> FJOIN <chan> <TS> <modes> :[[modes,]<uuid> [[modes,]<uuid> ... ]] * The last parameter is a list consisting of zero or more (modelist, uuid) * pairs (permanent channels may have zero users). The mode list for each * user is a concatenation of the mode letters the user has on the channel * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters * are not important but if a server ecounters an unknown mode letter, it will * drop the link to avoid desync. * * InspIRCd 2.0 and older required a comma before the uuid even if the user * had no prefix modes on the channel, InspIRCd 2.2 and later does not require * a comma in this case anymore. * */ time_t TS = ConvToInt(params[1]); if (!TS) { ServerInstance->Logs->Log("m_spanningtree",LOG_DEFAULT,"*** BUG? *** TS of 0 sent to FJOIN. Are some services authors smoking craq, or is it 1970 again?. Dropped."); ServerInstance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending FJOIN with a TS of zero. Total craq. Command was dropped.", srcuser->server.c_str()); return CMD_INVALID; } const std::string& channel = params[0]; Channel* chan = ServerInstance->FindChan(channel); bool apply_other_sides_modes = true; if (!chan) { chan = new Channel(channel, TS); } else { time_t ourTS = chan->age; if (TS != ourTS) { ServerInstance->SNO->WriteToSnoMask('d', "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %lu", chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (unsigned long)(ourTS - TS)); /* If our TS is less than theirs, we dont accept their modes */ if (ourTS < TS) { apply_other_sides_modes = false; } else if (ourTS > TS) { /* Our TS greater than theirs, clear all our modes from the channel, accept theirs. */ if (Utils->AnnounceTSChange) chan->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :TS for %s changed from %lu to %lu", chan->name.c_str(), channel.c_str(), (unsigned long) ourTS, (unsigned long) TS); // while the name is equal in case-insensitive compare, it might differ in case; use the remote version chan->name = channel; chan->age = TS; chan->ClearInvites(); CommandFJoin::RemoveStatus(chan); // XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it. // This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then // deleted later) as soon as the permchan mode is removed from them. if (ServerInstance->FindChan(channel) == NULL) { chan = new Channel(channel, TS); } } } } /* First up, apply their channel modes if they won the TS war */ if (apply_other_sides_modes) { std::vector<std::string> modelist; modelist.push_back(channel); /* Remember, params[params.size() - 1] is userlist, and we don't want to apply *that* */ modelist.insert(modelist.end(), params.begin()+2, params.end()-1); ServerInstance->SendMode(modelist, srcuser); } irc::modestacker modestack(true); TreeSocket* src_socket = Utils->FindServer(srcuser->server)->GetRoute()->GetSocket(); /* Now, process every 'modes,uuid' pair */ irc::tokenstream users(*params.rbegin()); std::string item; irc::modestacker* modestackptr = (apply_other_sides_modes ? &modestack : NULL); while (users.GetToken(item)) { if (!ProcessModeUUIDPair(item, src_socket, chan, modestackptr)) return CMD_INVALID; } /* Flush mode stacker if we lost the FJOIN or had equal TS */ if (apply_other_sides_modes) CommandFJoin::ApplyModeStack(srcuser, chan, modestack); return CMD_SUCCESS; }
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 +" + targuser->FormatNoticeMasks()); user->SendText(checkstr + " server " + targuser->server); 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 (IS_AWAY(targuser)) { /* user is away */ user->SendText(checkstr + " awaytime " + timestring(targuser->awaytime)); user->SendText(checkstr + " awaymsg " + targuser->awaymsg); } if (IS_OPER(targuser)) { OperInfo* oper = targuser->oper; /* user is an oper of type ____ */ user->SendText(checkstr + " opertype " + oper->NameStr()); 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 " + irc::sockets::satouser(loctarg->client_sa)); user->SendText(checkstr + " serveraddr " + irc::sockets::satouser(loctarg->server_sa)); 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; 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++) { char tmpbuf[MAXBUF]; /* * Unlike Asuka, I define a clone as coming from the same host. --w00t */ snprintf(tmpbuf, MAXBUF, "%-3lu %s%s (%s@%s) %s ", 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()); user->SendText(checkstr + " member " + tmpbuf); } irc::modestacker modestack(true); for(BanList::iterator b = targchan->bans.begin(); b != targchan->bans.end(); ++b) { modestack.Push('b', b->data); } std::vector<std::string> stackresult; std::vector<TranslateType> dummy; while (modestack.GetStackedLine(stackresult)) { creator->ProtoSendMode(user, TYPE_CHANNEL, targchan, stackresult, dummy); stackresult.clear(); } FOREACH_MOD(I_OnSyncChannel,OnSyncChannel(targchan,creator,user)); 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(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; }