/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2017 B00mX0r * Copyright (C) 2013-2014, 2016-2020 Sadie Powell * Copyright (C) 2013 Adam * Copyright (C) 2012-2016, 2018 Attila Molnar * Copyright (C) 2012, 2019 Robby * Copyright (C) 2009-2010 Daniel De Graaf * Copyright (C) 2007 Dennis Friis * Copyright (C) 2006-2009 Robin Burchell * Copyright (C) 2006-2008, 2010 Craig Edwards * * This file is part of InspIRCd. InspIRCd is free software: you can * redistribute it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "inspircd.h" #include "listmode.h" namespace { ChanModeReference ban(NULL, "ban"); } Channel::Channel(const std::string &cname, time_t ts) : name(cname) , age(ts) { if (!ServerInstance->Channels.GetChans().emplace(cname, this).second) throw CoreException("Cannot create duplicate channel " + cname); } void Channel::SetMode(ModeHandler* mh, bool on) { if (mh && mh->GetId() != ModeParser::MODEID_MAX) modes[mh->GetId()] = on; } void Channel::SetTopic(User* u, const std::string& ntopic, time_t topicts, const std::string* setter) { // Send a TOPIC message to the channel only if the new topic text differs if (this->topic != ntopic) { this->topic = ntopic; ClientProtocol::Messages::Topic topicmsg(u, this, this->topic); Write(ServerInstance->GetRFCEvents().topic, topicmsg); } // Always update setter and set time if (!setter) setter = ServerInstance->Config->FullHostInTopic ? &u->GetFullHost() : &u->nick; this->setby.assign(*setter, 0, ServerInstance->Config->Limits.GetMaxMask()); this->topicset = topicts; FOREACH_MOD(OnPostTopicChange, (u, this, this->topic)); } Membership* Channel::AddUser(User* user) { std::pair ret = userlist.emplace(user, insp::aligned_storage()); if (!ret.second) return NULL; Membership* memb = new(ret.first->second) Membership(user, this); return memb; } void Channel::DelUser(User* user) { MemberMap::iterator it = userlist.find(user); if (it != userlist.end()) DelUser(it); } void Channel::CheckDestroy() { if (!userlist.empty()) return; ModResult res; FIRST_MOD_RESULT(OnChannelPreDelete, res, (this)); if (res == MOD_RES_DENY) return; // If the channel isn't in chanlist then it is already in the cull list, don't add it again ChannelMap::iterator iter = ServerInstance->Channels.GetChans().find(this->name); if ((iter == ServerInstance->Channels.GetChans().end()) || (iter->second != this)) return; FOREACH_MOD(OnChannelDelete, (this)); ServerInstance->Channels.GetChans().erase(iter); ServerInstance->GlobalCulls.AddItem(this); } void Channel::DelUser(const MemberMap::iterator& membiter) { Membership* memb = membiter->second; memb->Cull(); memb->~Membership(); userlist.erase(membiter); // If this channel became empty then it should be removed CheckDestroy(); } Membership* Channel::GetUser(User* user) { MemberMap::iterator i = userlist.find(user); if (i == userlist.end()) return NULL; return i->second; } 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 (const auto& modechr : modeseq) { ModeHandler* mode = ServerInstance->Modes.FindMode(modechr, MODETYPE_CHANNEL); if (mode) { if (mode->IsPrefixMode()) continue; if (mode->NeedsParam(true)) { // If the parameter is missing or begins with a ':' then it's invalid if (!list.GetToken(parameter) || parameter[0] == ':') continue; } else { // The mode does not take a parameter. parameter.clear(); } Modes::Change modechange(mode, true, parameter); mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, modechange); } } } /* * add a channel to a user, creating the record for it if needed and linking * it to the user record */ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, const std::string& key) { if (user->registered != REG_ALL) { ServerInstance->Logs.Log("CHANNELS", LOG_DEBUG, "Attempted to join unregistered user " + user->uuid + " to channel " + cname); return NULL; } /* * We don't restrict the number of channels that remote users or users that are override-joining may be in. * We restrict local users to channels. * We restrict local operators to channels. * This is a lot more logical than how it was formerly. -- w00t */ if (!override) { unsigned long maxchans = user->GetClass()->maxchans; if (user->IsOper()) { unsigned long opermaxchans = ConvToNum(user->oper->getConfig("maxchans")); if (opermaxchans > maxchans) maxchans = opermaxchans; } if (user->chans.size() >= maxchans) { user->WriteNumeric(ERR_TOOMANYCHANNELS, cname, "You are on too many channels"); return NULL; } } // Crop channel name if it's too long if (cname.length() > ServerInstance->Config->Limits.MaxChannel) cname.resize(ServerInstance->Config->Limits.MaxChannel); Channel* chan = ServerInstance->Channels.Find(cname); bool created_by_local = !chan; // Flag that will be passed to ForceJoin later std::string privs; // Prefix mode(letter)s to give to the joining user if (!chan) { privs = ServerInstance->Config->DefaultModes.substr(0, ServerInstance->Config->DefaultModes.find(' ')); // Ask the modules whether they're ok with the join, pass NULL as Channel* as the channel is yet to be created ModResult MOD_RESULT; FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key, override)); if (!override && MOD_RESULT == MOD_RES_DENY) return nullptr; // A module wasn't happy with the join, abort chan = new Channel(cname, ServerInstance->Time()); // Set the default modes on the channel () chan->SetDefaultModes(); } else { /* Already on the channel */ if (chan->HasUser(user)) return nullptr; ModResult MOD_RESULT; FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, chan, cname, privs, key, override)); // A module explicitly denied the join and (hopefully) generated a message // describing the situation, so we may stop here without sending anything if (!override && MOD_RESULT == MOD_RES_DENY) return nullptr; } // We figured that this join is allowed and also created the // channel if it didn't exist before, now do the actual join chan->ForceJoin(user, &privs, false, created_by_local); return chan; } Membership* Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local) { if (IS_SERVER(user)) { ServerInstance->Logs.Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name); return NULL; } Membership* memb = this->AddUser(user); if (!memb) return NULL; // Already on the channel user->chans.push_front(memb); if (privs) { // If the user was granted prefix modes (in the OnUserPreJoin hook, or they're a // remote user and their own server set the modes), then set them internally now for (const auto& priv : *privs) { PrefixMode* mh = ServerInstance->Modes.FindPrefixMode(priv); if (mh) { // Set the mode on the user. Modes::Change modechange(mh, true, user->nick); mh->OnModeChange(ServerInstance->FakeClient, NULL, this, modechange); } } } // Tell modules about this join, they have the chance now to populate except_list with users we won't send the JOIN (and possibly MODE) to CUList except_list; FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list)); ClientProtocol::Events::Join joinevent(memb); this->Write(joinevent, 0, except_list); FOREACH_MOD(OnPostJoin, (memb)); return memb; } bool Channel::IsBanned(User* user) { ModResult result; FIRST_MOD_RESULT(OnCheckChannelBan, result, (user, this)); if (result != MOD_RES_PASSTHRU) return (result == MOD_RES_DENY); ListModeBase* banlm = static_cast(*ban); if (!banlm) return false; const ListModeBase::ModeList* bans = banlm->GetList(this); if (bans) { for (const auto& entry : *bans) { if (CheckBan(user, entry.mask)) return true; } } return false; } bool Channel::CheckBan(User* user, const std::string& mask) { ModResult result; FIRST_MOD_RESULT(OnCheckBan, result, (user, this, mask)); if (result != MOD_RES_PASSTHRU) return (result == MOD_RES_DENY); std::string::size_type at = mask.find('@'); if (at == std::string::npos) return false; const std::string nickIdent = user->nick + "!" + user->ident; std::string prefix(mask, 0, at); if (InspIRCd::Match(nickIdent, prefix, NULL)) { std::string suffix(mask, at + 1); if (InspIRCd::Match(user->GetRealHost(), suffix, NULL) || InspIRCd::Match(user->GetDisplayedHost(), suffix, NULL) || InspIRCd::MatchCIDR(user->GetIPString(), suffix, NULL)) return true; } return false; } /* Channel::PartUser * Remove a channel from a users record, remove the reference to the Membership object * from the channel and destroy it. */ bool Channel::PartUser(User* user, const std::string& reason) { MemberMap::iterator membiter = userlist.find(user); if (membiter == userlist.end()) return false; Membership* memb = membiter->second; std::string partreason(reason); CUList except_list; FOREACH_MOD(OnUserPart, (memb, partreason, except_list)); ClientProtocol::Messages::Part partmsg(memb, partreason); Write(ServerInstance->GetRFCEvents().part, partmsg, 0, except_list); // Remove this channel from the user's chanlist user->chans.erase(memb); // Remove the Membership from this channel's userlist and destroy it this->DelUser(membiter); return true; } void Channel::KickUser(User* src, const MemberMap::iterator& victimiter, const std::string& reason) { Membership* memb = victimiter->second; CUList except_list; FOREACH_MOD(OnUserKick, (src, memb, reason, except_list)); ClientProtocol::Messages::Kick kickmsg(src, memb, reason); Write(ServerInstance->GetRFCEvents().kick, kickmsg, 0, except_list); memb->user->chans.erase(memb); this->DelUser(victimiter); } void Channel::Write(ClientProtocol::Event& protoev, char status, const CUList& except_list) { unsigned int minrank = 0; if (status) { PrefixMode* mh = ServerInstance->Modes.FindPrefix(status); if (mh) minrank = mh->GetPrefixRank(); } for (const auto& [u, memb] : userlist) { LocalUser* user = IS_LOCAL(u); if ((user) && (!except_list.count(user))) { /* User doesn't have the status we're after */ if (minrank && memb->getRank() < minrank) continue; user->Send(protoev); } } } const char* Channel::ChanModes(bool showsecret) { static std::string scratch; std::string sparam; scratch.clear(); for (const auto& [_, mh] : ServerInstance->Modes.GetModes(MODETYPE_CHANNEL)) { if (IsModeSet(mh)) { scratch.push_back(mh->GetModeChar()); ParamModeBase* pm = mh->IsParameterMode(); if (!pm) continue; if (pm->IsParameterSecret() && !showsecret) { sparam += " <" + pm->name + ">"; } else { sparam += ' '; pm->GetParameter(this, sparam); } } } scratch += sparam; return scratch.c_str(); } void Channel::WriteNotice(const std::string& text, char status) { ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this, text, MSG_NOTICE, status); Write(ServerInstance->GetRFCEvents().privmsg, privmsg); } void Channel::WriteRemoteNotice(const std::string& text, char status) { WriteNotice(text, status); ServerInstance->PI->SendMessage(this, status, text, MSG_NOTICE); } /* returns the status character for a given user on a channel, e.g. @ for op, * % for halfop etc. If the user has several modes set, the highest mode * the user has must be returned. */ char Membership::GetPrefixChar() const { char pf = 0; unsigned int bestrank = 0; for (const auto& modechr : modes) { PrefixMode* mh = ServerInstance->Modes.FindPrefixMode(modechr); if (mh && mh->GetPrefixRank() > bestrank && mh->GetPrefix()) { bestrank = mh->GetPrefixRank(); pf = mh->GetPrefix(); } } return pf; } unsigned int Membership::getRank() { if (!modes.empty()) { PrefixMode* mh = ServerInstance->Modes.FindPrefixMode(modes[0]); if (mh) return mh->GetPrefixRank(); } return 0; } std::string Membership::GetAllPrefixChars() const { std::string ret; for (const auto& modechr : modes) { PrefixMode* mh = ServerInstance->Modes.FindPrefixMode(modechr); if (mh && mh->GetPrefix()) ret.push_back(mh->GetPrefix()); } return ret; } unsigned int Channel::GetPrefixValue(User* user) { MemberMap::iterator m = userlist.find(user); if (m == userlist.end()) return 0; return m->second->getRank(); } bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding) { char prefix = delta_mh->GetModeChar(); for (unsigned int i = 0; i < modes.length(); i++) { char mchar = modes[i]; PrefixMode* mh = ServerInstance->Modes.FindPrefixMode(mchar); if (mh && mh->GetPrefixRank() <= delta_mh->GetPrefixRank()) { modes = modes.substr(0,i) + (adding ? std::string(1, prefix) : "") + modes.substr(mchar == prefix ? i+1 : i); return adding != (mchar == prefix); } } if (adding) modes.push_back(prefix); return adding; } void Membership::WriteNotice(const std::string& text) const { LocalUser* const localuser = IS_LOCAL(user); if (!localuser) return; ClientProtocol::Messages::Privmsg privmsg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, this->chan, text, MSG_NOTICE); localuser->Send(ServerInstance->GetRFCEvents().privmsg, privmsg); }