1
0
mirror of https://github.com/anope/anope.git synced 2026-06-25 17:26:37 +02:00
Files
anope/src/servers.cpp
T
2012-04-23 05:08:26 -04:00

488 lines
12 KiB
C++

/* Routines to maintain a list of connected servers
*
* (C) 2003-2012 Anope Team
* Contact us at team@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*/
#include "services.h"
#include "modules.h"
#include "oper.h"
#include "servers.h"
#include "bots.h"
#include "regchannel.h"
#include "protocol.h"
#include "config.h"
#include "channels.h"
#include "extern.h"
/* Anope */
Server *Me = NULL;
std::set<Anope::string> Capab;
/** Constructor
* @param uplink The uplink this server is from, is only NULL when creating Me
* @param name The server name
* @param hops Hops from services server
* @param description Server rdescription
* @param sid Server sid/numeric
* @param flag An optional server flag
*/
Server::Server(Server *uplink, const Anope::string &name, unsigned hops, const Anope::string &description, const Anope::string &sid, ServerFlag flag) : Flags<ServerFlag>(ServerFlagStrings), Name(name), Hops(hops), Description(description), SID(sid), UplinkServer(uplink)
{
this->SetFlag(SERVER_SYNCING);
this->SetFlag(flag);
Log(this, "connect") << "uplinked to " << (this->UplinkServer ? this->UplinkServer->GetName() : "no uplink") << " connected to the network";
/* Add this server to our uplinks leaf list */
if (this->UplinkServer)
{
this->UplinkServer->AddLink(this);
/* Check to be sure this isn't a juped server */
if (Me == this->UplinkServer && !this->HasFlag(SERVER_JUPED))
{
/* Now do mode related stuff as we know what modes exist .. */
for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
{
BotInfo *bi = it->second;
Anope::string modes = !bi->botmodes.empty() ? ("+" + bi->botmodes) : ircd->pseudoclient_mode;
bi->SetModesInternal(modes.c_str());
for (unsigned i = 0; i < bi->botchannels.size(); ++i)
{
size_t h = bi->botchannels[i].find('#');
if (h == Anope::string::npos)
continue;
Anope::string chname = bi->botchannels[i].substr(h);
Channel *c = findchan(chname);
if (c && c->FindUser(bi))
{
Anope::string want_modes = bi->botchannels[i].substr(0, h);
for (unsigned j = 0; j < want_modes.length(); ++j)
{
ChannelMode *cm = ModeManager::FindChannelModeByChar(want_modes[j]);
if (cm == NULL)
cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_modes[j]));
if (cm && cm->Type == MODE_STATUS)
c->SetModeInternal(bi ? finduser(bi->nick) : NULL, cm, bi->nick);
}
}
}
}
ircdproto->SendBOB();
for (unsigned i = 0; i < Me->GetLinks().size(); ++i)
{
Server *s = Me->GetLinks()[i];
if (s->HasFlag(SERVER_JUPED))
ircdproto->SendServer(s);
}
/* We make the bots go online */
for (Anope::insensitive_map<User *>::iterator it = UserListByNick.begin(), it_end = UserListByNick.end(); it != it_end; ++it)
{
User *u = it->second;
ircdproto->SendClientIntroduction(u);
BotInfo *bi = findbot(u->nick);
if (bi)
{
bi->introduced = true;
XLine x(bi->nick, "Reserved for services");
ircdproto->SendSQLine(NULL, &x);
}
}
for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
{
Channel *c = it->second;
if (c->users.empty())
ircdproto->SendChannel(c);
else
for (CUserList::const_iterator cit = c->users.begin(), cit_end = c->users.end(); cit != cit_end; ++cit)
ircdproto->SendJoin((*cit)->user, c, (*cit)->Status);
}
}
}
}
/** Destructor
*/
Server::~Server()
{
Log(this, "quit") << "quit from " << (this->UplinkServer ? this->UplinkServer->GetName() : "no uplink") << " for " << this->QReason;
if (Capab.count("NOQUIT") > 0 || Capab.count("QS") > 0)
{
for (Anope::insensitive_map<User *>::const_iterator it = UserListByNick.begin(); it != UserListByNick.end();)
{
User *u = it->second;
++it;
if (u->server == this)
{
NickAlias *na = findnick(u->nick);
if (na && !na->nc->HasFlag(NI_SUSPENDED) && (u->IsRecognized() || u->IsIdentified()))
{
na->last_seen = Anope::CurTime;
na->last_quit = this->QReason;
}
delete u;
}
}
Log(LOG_DEBUG) << "Finished removing all users for " << this->GetName();
}
if (this->UplinkServer)
this->UplinkServer->DelLink(this);
for (unsigned i = this->Links.size(); i > 0; --i)
this->Links[i - 1]->Delete(this->QReason);
}
/** Delete this server with a reason
* @param reason The reason
*/
void Server::Delete(const Anope::string &reason)
{
this->QReason = reason;
delete this;
}
/** Get the name for this server
* @return The name
*/
const Anope::string &Server::GetName() const
{
return this->Name;
}
/** Get the number of hops this server is from services
* @return Number of hops
*/
unsigned Server::GetHops() const
{
return this->Hops;
}
/** Set the server description
* @param desc The new description
*/
void Server::SetDescription(const Anope::string &desc)
{
this->Description = desc;
}
/** Get the server description
* @return The server description
*/
const Anope::string &Server::GetDescription() const
{
return this->Description;
}
/** Change this servers SID
* @param sid The new SID
*/
void Server::SetSID(const Anope::string &sid)
{
this->SID = sid;
}
/** Get the server numeric/SID
* @return The numeric/SID
*/
const Anope::string &Server::GetSID() const
{
return this->SID;
}
/** Get the list of links this server has, or NULL if it has none
* @return A list of servers
*/
const std::vector<Server *> &Server::GetLinks() const
{
return this->Links;
}
/** Get the uplink server for this server, if this is our uplink will be Me
* @return The servers uplink
*/
Server *Server::GetUplink()
{
return this->UplinkServer;
}
/** Adds a link to this server
* @param s The linking server
*/
void Server::AddLink(Server *s)
{
this->Links.push_back(s);
Log(this, "connect") << "introduced " << s->GetName();
}
/** Delinks a server from this server
* @param s The server
*/
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();
}
/** Finish syncing this server and optionally all links to it
* @param SyncLinks True to sync the links for this server too (if any)
*/
void Server::Sync(bool SyncLinks)
{
if (this->IsSynced())
return;
this->UnsetFlag(SERVER_SYNCING);
Log(this, "sync") << "is done syncing";
FOREACH_MOD(I_OnServerSync, OnServerSync(this));
if (SyncLinks && !this->Links.empty())
{
for (unsigned i = 0, j = this->Links.size(); i < j; ++i)
this->Links[i]->Sync(true);
}
if (this->GetUplink() && this->GetUplink() == Me)
{
for (registered_channel_map::iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it)
{
ChannelInfo *ci = it->second;
if (ci->HasFlag(CI_PERSIST))
{
bool created = false;
if (!ci->c)
{
ci->c = new Channel(ci->name, ci->time_registered);
created = true;
}
if (ModeManager::FindChannelModeByName(CMODE_PERM) != NULL)
{
ci->c->SetMode(NULL, CMODE_PERM);
if (created)
ircdproto->SendChannel(ci->c);
}
else
{
if (!ci->bi)
ci->WhoSends()->Assign(NULL, ci);
if (ci->c->FindUser(ci->bi) == NULL)
ci->bi->Join(ci->c);
}
}
}
FOREACH_MOD(I_OnPreUplinkSync, OnPreUplinkSync(this));
ircdproto->SendEOB();
Me->Sync(false);
FOREACH_MOD(I_OnUplinkSync, OnUplinkSync(this));
for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it)
{
Channel *c = it->second;
c->CheckModes();
if (c->ci)
c->ci->RestoreTopic();
}
if (!nofork && AtTerm())
{
Log(LOG_TERMINAL) << "Successfully linked, launching into background...";
Fork();
}
}
}
/** Check if this server is synced
* @return true or false
*/
bool Server::IsSynced() const
{
return !this->HasFlag(SERVER_SYNCING);
}
/** Check if this server is ULined
* @return true or false
*/
bool Server::IsULined() const
{
if (this == Me)
return true;
for (std::list<Anope::string>::const_iterator it = Config->Ulines.begin(), it_end = Config->Ulines.end(); it != it_end; ++it)
if (it->equals_ci(this->GetName()))
return true;
return false;
}
/** Send a message to alll users on this server
* @param source The source of the message
* @param message The message
*/
void Server::Notice(const BotInfo *source, const Anope::string &message)
{
if (Config->NSDefFlags.HasFlag(NI_MSG))
ircdproto->SendGlobalPrivmsg(source, this, message);
else
ircdproto->SendGlobalNotice(source, this, message);
}
/** Find a server
* @param name The name or SID/numeric
* @param s The server list to search for this server on, defaults to our Uplink
* @return The server
*/
Server *Server::Find(const Anope::string &name, Server *s)
{
Log(LOG_DEBUG_2) << "Server::Find called for " << name;
if (!s)
s = Me;
if (s->GetName().equals_ci(name) || s->GetSID().equals_cs(name))
return s;
if (!s->GetLinks().empty())
{
for (unsigned i = 0, j = s->GetLinks().size(); i < j; ++i)
{
Server *serv = s->GetLinks()[i];
if (serv->GetName().equals_ci(name) || serv->GetSID().equals_cs(name))
return serv;
Log(LOG_DEBUG_2) << "Server::Find checking " << serv->GetName() << " server tree for " << name;
Server *server = Server::Find(name, serv);
if (server)
return server;
}
}
return NULL;
}
/*************************************************************************/
/**
* Handle adding the server to the Server struct
* @param source Name of the uplink if any
* @param servername Name of the server being linked
* @param hops Number of hops to reach this server
* @param descript Description of the server
* @param numeric Server Numberic/SUID
* @return void
*/
void do_server(const Anope::string &source, const Anope::string &servername, unsigned int hops, const Anope::string &descript, const Anope::string &numeric)
{
if (source.empty())
Log(LOG_DEBUG) << "Server " << servername << " introduced";
else
Log(LOG_DEBUG) << "Server introduced (" << servername << ")" << " from " << source;
Server *s = NULL;
if (!source.empty())
{
s = Server::Find(source);
if (!s)
throw CoreException("Recieved a server from a nonexistant uplink?");
}
else
s = Me;
/* Create a server structure. */
Server *newserver = new Server(s, servername, hops, descript, numeric);
/* Let modules know about the connection */
FOREACH_MOD(I_OnNewServer, OnNewServer(newserver));
}
static inline char nextID(char &c)
{
if (c == 'Z')
c = '0';
else if (c != '9')
++c;
else
c = 'A';
return c;
}
/** Recieve the next UID in our list
* @return The UID
*/
const Anope::string ts6_uid_retrieve()
{
if (!ircd || !ircd->ts6)
return "";
static Anope::string current_uid = "AAAAAA";
while (finduser(Config->Numeric + current_uid) != NULL)
{
int current_len = current_uid.length() - 1;
while (current_len >= 0 && nextID(current_uid[current_len--]) == 'A');
}
return Config->Numeric + current_uid;
}
/** Return the next SID in our list
* @return The SID
*/
const Anope::string ts6_sid_retrieve()
{
if (!ircd || !ircd->ts6)
return "";
static Anope::string current_sid;
if (current_sid.empty())
{
current_sid = Config->Numeric;
if (current_sid.empty())
current_sid = "00A";
}
while (Server::Find(current_sid) != NULL)
{
int current_len = current_sid.length() - 1;
while (current_len >= 0 && nextID(current_sid[current_len--]) == 'A');
}
return current_sid;
}