mirror of
https://github.com/anope/anope.git
synced 2026-06-25 11:36:37 +02:00
375 lines
7.5 KiB
C++
375 lines
7.5 KiB
C++
// Anope IRC Services <https://www.anope.org/>
|
|
//
|
|
// Copyright (C) 2003-2026 Anope Contributors
|
|
//
|
|
// Anope is free software. You can use, modify, and/or distribute it under the
|
|
// terms of version 2 of the GNU General Public License. See docs/LICENSE.txt
|
|
// for the complete terms of this license and docs/AUTHORS.txt for a list of
|
|
// contributors.
|
|
//
|
|
// Based on the original code of Epona by Lara
|
|
// Based on the original code of Services by Andy Church
|
|
//
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
#include "services.h"
|
|
#include "modules.h"
|
|
#include "xline.h"
|
|
#include "servers.h"
|
|
#include "bots.h"
|
|
#include "regchannel.h"
|
|
#include "protocol.h"
|
|
#include "config.h"
|
|
#include "channels.h"
|
|
|
|
/* Anope */
|
|
Server *Me = NULL;
|
|
|
|
Anope::map<Server *> Servers::ByName;
|
|
Anope::map<Server *> Servers::ByID;
|
|
|
|
std::set<Anope::string> Servers::Capab;
|
|
|
|
Server::Server(Server *up, const Anope::string &sname, const Anope::string &desc, const Anope::string &ssid, unsigned shops, bool jupe)
|
|
: name(sname)
|
|
, hops(shops)
|
|
, description(desc)
|
|
, sid(ssid)
|
|
, uplink(up)
|
|
, juped(jupe)
|
|
{
|
|
Servers::ByName[sname] = this;
|
|
if (!ssid.empty())
|
|
Servers::ByID[ssid] = this;
|
|
|
|
Log(this, "connect") << "has connected to the network (uplinked to " << (this->uplink ? this->uplink->GetName() : "no uplink") << ")";
|
|
|
|
/* Add this server to our uplinks leaf list */
|
|
if (this->uplink)
|
|
{
|
|
this->uplink->AddLink(this);
|
|
|
|
/* Check to be sure this isn't a juped server */
|
|
if (Me == this->uplink && !juped)
|
|
{
|
|
/* Now do mode related stuff as we know what modes exist .. */
|
|
for (auto &[_, bi] : *BotListByNick)
|
|
{
|
|
spacesepstream modesep(bi->botmodes.empty() ? IRCD->DefaultPseudoclientModes : "+" + bi->botmodes);
|
|
|
|
Anope::string modechars;
|
|
modesep.GetToken(modechars);
|
|
|
|
std::vector<Anope::string> modeparams;
|
|
modesep.GetTokens(modeparams);
|
|
|
|
bi->SetModesInternal(bi, modechars, modeparams);
|
|
for (const auto &botchannel : bi->botchannels)
|
|
{
|
|
size_t h = botchannel.find('#');
|
|
if (h == Anope::string::npos)
|
|
continue;
|
|
Anope::string chname = botchannel.substr(h);
|
|
Channel *c = Channel::Find(chname);
|
|
if (c && c->FindUser(bi))
|
|
{
|
|
Anope::string want_modes = botchannel.substr(0, h);
|
|
for (char want_mode : want_modes)
|
|
{
|
|
ChannelMode *cm = ModeManager::FindChannelModeByChar(want_mode);
|
|
if (cm == NULL)
|
|
cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_mode));
|
|
if (cm && cm->type == MODE_STATUS)
|
|
{
|
|
MessageSource ms = bi;
|
|
c->SetModeInternal(ms, cm, bi->nick);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IRCD->SendBOB();
|
|
|
|
for (auto *link : Me->GetLinks())
|
|
{
|
|
if (link->juped)
|
|
IRCD->SendServer(link);
|
|
}
|
|
|
|
/* We make the bots go online */
|
|
for (const auto &[_, u] : UserListByNick)
|
|
{
|
|
BotInfo *bi = BotInfo::Find(u->GetUID());
|
|
if (bi)
|
|
{
|
|
XLine x(bi->nick, "Reserved for services");
|
|
IRCD->SendSQLine(NULL, &x);
|
|
}
|
|
|
|
IRCD->SendClientIntroduction(u);
|
|
if (bi)
|
|
bi->introduced = true;
|
|
}
|
|
|
|
for (const auto &[_, c] : ChannelList)
|
|
{
|
|
if (c->users.empty())
|
|
IRCD->SendChannel(c);
|
|
else
|
|
{
|
|
for (const auto &[_, uc] : c->users)
|
|
IRCD->SendJoin(uc->user, c, &uc->status);
|
|
}
|
|
|
|
for (const auto &[mode, data] : c->GetModes())
|
|
{
|
|
ChannelMode *cm = ModeManager::FindChannelModeByName(mode);
|
|
if (!cm || cm->type != MODE_LIST)
|
|
continue;
|
|
ModeManager::StackerAdd(c->WhoSends(), c, cm, true, data);
|
|
}
|
|
|
|
if (!c->topic.empty() && !c->topic_setter.empty())
|
|
IRCD->SendTopic(c->WhoSends(), c);
|
|
|
|
c->syncing = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
FOREACH_MOD(OnNewServer, (this));
|
|
}
|
|
|
|
Server::~Server()
|
|
{
|
|
Log(this, "quit") << "quit from " << (this->uplink ? this->uplink->GetName() : "no uplink") << " for " << this->quit_reason;
|
|
|
|
for (const auto &[_, u] : UserListByNick)
|
|
{
|
|
if (u->server == this)
|
|
{
|
|
u->Quit(this->quit_reason);
|
|
u->server = NULL;
|
|
}
|
|
}
|
|
|
|
Log(LOG_DEBUG) << "Finished removing all users for " << this->GetName();
|
|
|
|
if (this->uplink)
|
|
this->uplink->DelLink(this);
|
|
|
|
for (unsigned i = this->links.size(); i > 0; --i)
|
|
this->links[i - 1]->Delete(this->quit_reason);
|
|
|
|
Servers::ByName.erase(this->name);
|
|
if (!this->sid.empty())
|
|
Servers::ByID.erase(this->sid);
|
|
}
|
|
|
|
void Server::Delete(const Anope::string &reason)
|
|
{
|
|
this->quit_reason = reason;
|
|
this->quitting = true;
|
|
FOREACH_MOD(OnServerQuit, (this));
|
|
delete this;
|
|
}
|
|
|
|
const Anope::string &Server::GetName() const
|
|
{
|
|
return this->name;
|
|
}
|
|
|
|
unsigned Server::GetHops() const
|
|
{
|
|
return this->hops;
|
|
}
|
|
|
|
void Server::SetDescription(const Anope::string &desc)
|
|
{
|
|
this->description = desc;
|
|
}
|
|
|
|
const Anope::string &Server::GetDescription() const
|
|
{
|
|
return this->description;
|
|
}
|
|
|
|
void Server::SetSID(const Anope::string &nsid)
|
|
{
|
|
if (!this->sid.empty())
|
|
throw CoreException("Server already has an id?");
|
|
this->sid = nsid;
|
|
Servers::ByID[nsid] = this;
|
|
}
|
|
|
|
const Anope::string &Server::GetSID() const
|
|
{
|
|
if (!this->sid.empty() && IRCD->RequiresID)
|
|
return this->sid;
|
|
else
|
|
return this->name;
|
|
}
|
|
|
|
const Anope::string &Server::GetQuitReason() const
|
|
{
|
|
return this->quit_reason;
|
|
}
|
|
|
|
const std::vector<Server *> &Server::GetLinks() const
|
|
{
|
|
return this->links;
|
|
}
|
|
|
|
Server *Server::GetUplink()
|
|
{
|
|
return this->uplink;
|
|
}
|
|
|
|
void Server::AddLink(Server *s)
|
|
{
|
|
this->links.push_back(s);
|
|
|
|
Log(this, "connect") << "introduced " << s->GetName();
|
|
}
|
|
|
|
void Server::DelLink(Server *s)
|
|
{
|
|
if (this->links.empty())
|
|
throw CoreException("Server::DelLink called on " + this->GetName() + " for " + s->GetName() + " but we have no links?");
|
|
|
|
for (unsigned i = 0, j = this->links.size(); i < j; ++i)
|
|
{
|
|
if (this->links[i] == s)
|
|
{
|
|
this->links.erase(this->links.begin() + i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Log(this, "quit") << "quit " << s->GetName();
|
|
}
|
|
|
|
void Server::Sync(bool sync_links)
|
|
{
|
|
if (this->IsSynced())
|
|
return;
|
|
|
|
syncing = false;
|
|
|
|
Log(this, "sync") << "is done syncing";
|
|
|
|
FOREACH_MOD(OnServerSync, (this));
|
|
|
|
if (sync_links && !this->links.empty())
|
|
{
|
|
for (auto *link : this->links)
|
|
link->Sync(true);
|
|
}
|
|
|
|
bool me = this->GetUplink() && this->GetUplink() == Me;
|
|
|
|
if (me)
|
|
{
|
|
FOREACH_MOD(OnPreUplinkSync, (this));
|
|
}
|
|
|
|
for (auto it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end;)
|
|
{
|
|
Channel *c = it->second;
|
|
++it;
|
|
|
|
if (c->syncing)
|
|
c->Sync();
|
|
}
|
|
|
|
if (me)
|
|
{
|
|
IRCD->SendEOB();
|
|
Me->Sync(false);
|
|
|
|
FOREACH_MOD(OnUplinkSync, (this));
|
|
|
|
for (const auto &[_, c] : ChannelList)
|
|
{
|
|
if (c->syncing)
|
|
c->Sync();
|
|
}
|
|
|
|
if (!Anope::NoFork)
|
|
{
|
|
Log(LOG_TERMINAL) << "Successfully linked, launching into background...";
|
|
Anope::Fork();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Server::IsSynced() const
|
|
{
|
|
return !syncing;
|
|
}
|
|
|
|
void Server::Unsync()
|
|
{
|
|
syncing = true;
|
|
}
|
|
|
|
bool Server::IsULined() const
|
|
{
|
|
if (this == Me)
|
|
return true;
|
|
|
|
for (const auto &uline : Config->Ulines)
|
|
{
|
|
if (uline.equals_ci(this->GetName()))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Server::IsJuped() const
|
|
{
|
|
return juped;
|
|
}
|
|
|
|
bool Server::IsQuitting() const
|
|
{
|
|
return quitting;
|
|
}
|
|
|
|
void Server::Notice(BotInfo *source, const Anope::string &message)
|
|
{
|
|
if (Config->DefPrivmsg)
|
|
IRCD->SendGlobalPrivmsg(source, this, message);
|
|
else
|
|
IRCD->SendGlobalNotice(source, this, message);
|
|
}
|
|
|
|
Server *Server::Find(const Anope::string &name, bool name_only)
|
|
{
|
|
Anope::map<Server *>::iterator it;
|
|
|
|
if (!name_only)
|
|
{
|
|
it = Servers::ByID.find(name);
|
|
if (it != Servers::ByID.end())
|
|
return it->second;
|
|
}
|
|
|
|
it = Servers::ByName.find(name);
|
|
if (it != Servers::ByName.end())
|
|
return it->second;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Server *Servers::GetUplink()
|
|
{
|
|
for (auto *link : Me->GetLinks())
|
|
{
|
|
if (!link->IsJuped())
|
|
return link;
|
|
}
|
|
return NULL;
|
|
}
|