void Channel::SetDefaultModes() { ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s", ServerInstance->Config->DefaultModes.c_str()); irc::spacesepstream list(ServerInstance->Config->DefaultModes); std::string modeseq; std::string parameter; list.GetToken(modeseq); for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n) { ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL); if (mode) { if (mode->IsPrefixMode()) continue; if (mode->NeedsParam(true)) { list.GetToken(parameter); // If the parameter begins with a ':' then it's invalid if (parameter.c_str()[0] == ':') continue; } else parameter.clear(); if ((mode->NeedsParam(true)) && (parameter.empty())) continue; mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true); } } }
void Channel::SetDefaultModes() { ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s", ServerInstance->Config->DefaultModes.c_str()); irc::spacesepstream list(ServerInstance->Config->DefaultModes); std::string modeseq; std::string parameter; list.GetToken(modeseq); for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n) { ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL); if (mode) { if (mode->IsPrefixMode()) continue; if (mode->GetNumParams(true)) list.GetToken(parameter); else parameter.clear(); mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true); } } }
void LoadDatabase() { /* * Process config-defined list of permanent channels. * -- w00t */ ConfigTagList permchannels = ServerInstance->Config->ConfTags("permchannels"); for (ConfigIter i = permchannels.first; i != permchannels.second; ++i) { ConfigTag* tag = i->second; std::string channel = tag->getString("channel"); std::string modes = tag->getString("modes"); if ((channel.empty()) || (channel.length() > ServerInstance->Config->Limits.ChanMax)) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Ignoring permchannels tag with empty or too long channel name (\"" + channel + "\")"); continue; } Channel *c = ServerInstance->FindChan(channel); if (!c) { time_t TS = tag->getInt("ts", ServerInstance->Time(), 1); c = new Channel(channel, TS); unsigned int topicset = tag->getInt("topicts"); c->topic = tag->getString("topic"); if ((topicset != 0) || (!c->topic.empty())) { if (topicset == 0) topicset = ServerInstance->Time(); c->topicset = topicset; c->setby = tag->getString("topicsetby"); if (c->setby.empty()) c->setby = ServerInstance->Config->ServerName; } ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Added %s with topic %s", channel.c_str(), c->topic.c_str()); if (modes.empty()) continue; irc::spacesepstream list(modes); std::string modeseq; std::string par; list.GetToken(modeseq); // XXX bleh, should we pass this to the mode parser instead? ugly. --w00t for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n) { ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL); if (mode) { if (mode->GetNumParams(true)) list.GetToken(par); else par.clear(); mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, c, par, true); } } } } }
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; }
void LoadDatabase() { /* * Process config-defined list of permanent channels. * -- w00t */ ConfigTagList permchannels = ServerInstance->Config->ConfTags("permchannels"); for (ConfigIter i = permchannels.first; i != permchannels.second; ++i) { ConfigTag* tag = i->second; std::string channel = tag->getString("channel"); std::string topic = tag->getString("topic"); std::string modes = tag->getString("modes"); if (channel.empty()) { ServerInstance->Logs->Log("m_permchannels", LOG_DEBUG, "Malformed permchannels tag with empty channel name."); continue; } Channel *c = ServerInstance->FindChan(channel); if (!c) { c = new Channel(channel, ServerInstance->Time()); if (!topic.empty()) { c->SetTopic(ServerInstance->FakeClient, topic, true); /* * Due to the way protocol works in 1.2, we need to hack the topic TS in such a way that this * topic will always win over others. * * This is scheduled for (proper) fixing in a later release, and can be removed at a later date. */ c->topicset = 42; } ServerInstance->Logs->Log("m_permchannels", LOG_DEBUG, "Added %s with topic %s", channel.c_str(), topic.c_str()); if (modes.empty()) continue; irc::spacesepstream list(modes); std::string modeseq; std::string par; list.GetToken(modeseq); // XXX bleh, should we pass this to the mode parser instead? ugly. --w00t for (std::string::iterator n = modeseq.begin(); n != modeseq.end(); ++n) { ModeHandler* mode = ServerInstance->Modes->FindMode(*n, MODETYPE_CHANNEL); if (mode) { if (mode->GetNumParams(true)) list.GetToken(par); else par.clear(); mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, c, par, true); } } } } }
CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::string>& params) { /** Do we have enough parameters: * 0 1 2 3 4 5 6 7 8 9 (n-1) * UID uuid age nick host dhost ident ip.string signon +modes (modepara) :gecos */ time_t age_t = ConvToInt(params[1]); time_t signon = ConvToInt(params[7]); std::string empty; const std::string& modestr = params[8]; /* Is this a valid UID, and not misrouted? */ if (params[0].length() != UIDGenerator::UUID_LENGTH || params[0].substr(0, 3) != remoteserver->GetID()) return CMD_INVALID; /* Check parameters for validity before introducing the client, discovered by dmb */ if (!age_t) return CMD_INVALID; if (!signon) return CMD_INVALID; if (modestr[0] != '+') return CMD_INVALID; /* check for collision */ user_hash::iterator iter = ServerInstance->Users->clientlist->find(params[2]); if (iter != ServerInstance->Users->clientlist->end()) { /* * Nick collision. */ int collide = Utils->DoCollision(iter->second, remoteserver, age_t, params[5], params[6], params[0]); ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "*** Collision on %s, collide=%d", params[2].c_str(), collide); if (collide != 1) { // Remote client lost, make sure we change their nick for the hash too params[2] = params[0]; } } /* IMPORTANT NOTE: For remote users, we pass the UUID in the constructor. This automatically * sets it up in the UUID hash for us. */ User* _new = NULL; try { _new = new RemoteUser(params[0], remoteserver); } catch (...) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Duplicate UUID %s in client introduction", params[0].c_str()); return CMD_INVALID; } (*(ServerInstance->Users->clientlist))[params[2]] = _new; _new->nick = params[2]; _new->host = params[3]; _new->dhost = params[4]; _new->ident = params[5]; _new->fullname = params[params.size() - 1]; _new->registered = REG_ALL; _new->signon = signon; _new->age = age_t; unsigned int paramptr = 9; for (std::string::const_iterator v = modestr.begin(); v != modestr.end(); ++v) { // Accept more '+' chars, for now if (*v == '+') continue; /* For each mode thats set, find the mode handler and set it on the new user */ ModeHandler* mh = ServerInstance->Modes->FindMode(*v, MODETYPE_USER); if (!mh) { ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Unrecognised mode '%c' for a user in UID, dropping link", *v); return CMD_INVALID; } if (mh->GetNumParams(true)) { if (paramptr >= params.size() - 1) return CMD_INVALID; std::string mp = params[paramptr++]; /* IMPORTANT NOTE: * All modes are assumed to succeed here as they are being set by a remote server. * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important * to note as all but one modules currently cannot ever fail in this situation, except for * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE * but here, at client introduction. You may safely assume this behaviour is standard and * will not change in future versions if you want to make use of this protective behaviour * yourself. */ mh->OnModeChange(_new, _new, NULL, mp, true); } else mh->OnModeChange(_new, _new, NULL, empty, true); _new->SetMode(mh, true); } _new->SetClientIP(params[6].c_str()); ServerInstance->Users->AddGlobalClone(_new); remoteserver->UserCount++; bool dosend = true; if ((Utils->quiet_bursts && remoteserver->bursting) || _new->server->IsSilentULine()) dosend = false; if (dosend) ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", remoteserver->GetName().c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString().c_str(), _new->fullname.c_str()); FOREACH_MOD(OnPostConnect, (_new)); return CMD_SUCCESS; }
CmdResult CommandUID::HandleServer(TreeServer* remoteserver, std::vector<std::string>& params) { /** * 0 1 2 3 4 5 6 7 8 9 (n-1) * UID uuid age nick host dhost ident ip.string signon +modes (modepara) :gecos */ time_t age_t = ServerCommand::ExtractTS(params[1]); time_t signon = ServerCommand::ExtractTS(params[7]); std::string empty; const std::string& modestr = params[8]; // Check if the length of the uuid is correct and confirm the sid portion of the uuid matches the sid of the server introducing the user if (params[0].length() != UIDGenerator::UUID_LENGTH || params[0].compare(0, 3, remoteserver->GetID())) throw ProtocolException("Bogus UUID"); // Sanity check on mode string: must begin with '+' if (modestr[0] != '+') throw ProtocolException("Invalid mode string"); // See if there is a nick collision User* collideswith = ServerInstance->FindNickOnly(params[2]); if ((collideswith) && (collideswith->registered != REG_ALL)) { // User that the incoming user is colliding with is not fully registered, we force nick change the // unregistered user to their uuid and tell them what happened collideswith->WriteFrom(collideswith, "NICK %s", collideswith->uuid.c_str()); collideswith->WriteNumeric(ERR_NICKNAMEINUSE, collideswith->nick, "Nickname overruled."); // Clear the bit before calling User::ChangeNick() to make it NOT run the OnUserPostNick() hook collideswith->registered &= ~REG_NICK; collideswith->ChangeNick(collideswith->uuid); } else if (collideswith) { // The user on this side is registered, handle the collision bool they_change = Utils->DoCollision(collideswith, remoteserver, age_t, params[5], params[6], params[0], "UID"); if (they_change) { // The client being introduced needs to change nick to uuid, change the nick in the message before // processing/forwarding it. Also change the nick TS to CommandSave::SavedTimestamp. age_t = CommandSave::SavedTimestamp; params[1] = ConvToStr(CommandSave::SavedTimestamp); params[2] = params[0]; } } /* For remote users, we pass the UUID they sent to the constructor. * If the UUID already exists User::User() throws an exception which causes this connection to be closed. */ RemoteUser* _new = new SpanningTree::RemoteUser(params[0], remoteserver); ServerInstance->Users->clientlist[params[2]] = _new; _new->nick = params[2]; _new->host = params[3]; _new->dhost = params[4]; _new->ident = params[5]; _new->fullname = params.back(); _new->registered = REG_ALL; _new->signon = signon; _new->age = age_t; unsigned int paramptr = 9; for (std::string::const_iterator v = modestr.begin(); v != modestr.end(); ++v) { // Accept more '+' chars, for now if (*v == '+') continue; /* For each mode thats set, find the mode handler and set it on the new user */ ModeHandler* mh = ServerInstance->Modes->FindMode(*v, MODETYPE_USER); if (!mh) throw ProtocolException("Unrecognised mode '" + std::string(1, *v) + "'"); if (mh->NeedsParam(true)) { if (paramptr >= params.size() - 1) throw ProtocolException("Out of parameters while processing modes"); std::string mp = params[paramptr++]; /* IMPORTANT NOTE: * All modes are assumed to succeed here as they are being set by a remote server. * Modes CANNOT FAIL here. If they DO fail, then the failure is ignored. This is important * to note as all but one modules currently cannot ever fail in this situation, except for * m_servprotect which specifically works this way to prevent the mode being set ANYWHERE * but here, at client introduction. You may safely assume this behaviour is standard and * will not change in future versions if you want to make use of this protective behaviour * yourself. */ mh->OnModeChange(_new, _new, NULL, mp, true); } else mh->OnModeChange(_new, _new, NULL, empty, true); _new->SetMode(mh, true); } _new->SetClientIP(params[6].c_str()); ServerInstance->Users->AddClone(_new); remoteserver->UserCount++; bool dosend = true; if ((Utils->quiet_bursts && remoteserver->IsBehindBursting()) || _new->server->IsSilentULine()) dosend = false; if (dosend) ServerInstance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s (%s) [%s]", remoteserver->GetName().c_str(), _new->GetFullRealHost().c_str(), _new->GetIPString().c_str(), _new->fullname.c_str()); FOREACH_MOD(OnPostConnect, (_new)); return CMD_SUCCESS; }