/*
* Anope IRC Services
*
* Copyright (C) 2005-2016 Anope Team <team@anope.org>
*
* This file is part of Anope. Anope 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 see <http://www.gnu.org/licenses/>.
*/
#include "module.h"
#include "modules/sasl.h"
#include "modules/chanserv/mode.h"
#include "modules/chanserv/set.h"
struct SASLUser
{
Anope::string uid;
Anope::string acc;
time_t created;
};
static std::list<SASLUser> saslusers;
static Anope::string rsquit_server, rsquit_id;
static unsigned int spanningtree_proto_ver = 0;
class InspIRCd20Proto : public IRCDProto
{
private:
void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) override
{
IRCDProto::SendSVSKillInternal(source, user, buf);
user->KillInternal(source, buf);
}
void SendChgIdentInternal(const Anope::string &nick, const Anope::string &vIdent)
{
if (!Servers::Capab.count("CHGIDENT"))
Log() << "CHGIDENT not loaded!";
else
UplinkSocket::Message(Me) << "CHGIDENT " << nick << " " << vIdent;
}
void SendChgHostInternal(const Anope::string &nick, const Anope::string &vhost)
{
if (!Servers::Capab.count("CHGHOST"))
Log() << "CHGHOST not loaded!";
else
UplinkSocket::Message(Me) << "CHGHOST " << nick << " " << vhost;
}
void SendAddLine(const Anope::string &xtype, const Anope::string &mask, time_t duration, const Anope::string &addedby, const Anope::string &reason)
{
UplinkSocket::Message(Me) << "ADDLINE " << xtype << " " << mask << " " << addedby << " " << Anope::CurTime << " " << duration << " :" << reason;
}
void SendDelLine(const Anope::string &xtype, const Anope::string &mask)
{
UplinkSocket::Message(Me) << "DELLINE " << xtype << " " << mask;
}
public:
InspIRCd20Proto(Module *creator) : IRCDProto(creator, "InspIRCd 2.0")
{
DefaultPseudoclientModes = "+I";
CanSVSNick = true;
CanSVSJoin = true;
CanSetVHost = true;
CanSetVIdent = true;
CanSQLine = true;
CanSZLine = true;
CanSVSHold = true;
CanCertFP = true;
RequiresID = true;
MaxModes = 20;
}
void SendConnect() override
{
UplinkSocket::Message() << "CAPAB START 1202";
UplinkSocket::Message() << "CAPAB CAPABILITIES :PROTOCOL=1202";
UplinkSocket::Message() << "CAPAB END";
SendServer(Me);
}
void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
UplinkSocket::Message(bi) << "NOTICE $" << dest->GetName() << " :" << msg;
}
void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
UplinkSocket::Message(bi) << "PRIVMSG $" << dest->GetName() << " :" << msg;
}
void SendAkillDel(XLine *x) override
{
/* InspIRCd may support regex bans */
if (x->IsRegex() && Servers::Capab.count("RLINE"))
{
Anope::string mask = x->GetMask();
size_t h = mask.find('#');
if (h != Anope::string::npos)
mask = mask.replace(h, 1, ' ');
SendDelLine("R", mask);
return;
}
else if (x->IsRegex() || x->HasNickOrReal())
return;
/* ZLine if we can instead */
if (x->GetUser() == "*")
{
cidr addr(x->GetHost());
if (addr.valid())
{
IRCD->SendSZLineDel(x);
return;
}
}
SendDelLine("G", x->GetUser() + "@" + x->GetHost());
}
void SendTopic(const MessageSource &source, Channel *c) override
{
if (Servers::Capab.count("SVSTOPIC"))
{
UplinkSocket::Message(c->ci->WhoSends()) << "SVSTOPIC " << c->name << " " << c->topic_ts << " " << c->topic_setter << " :" << c->topic;
}
else
{
/* If the last time a topic was set is after the TS we want for this topic we must bump this topic's timestamp to now */
time_t ts = c->topic_ts;
if (c->topic_time > ts)
ts = Anope::CurTime;
/* But don't modify c->topic_ts, it should remain set to the real TS we want as ci->last_topic_time pulls from it */
UplinkSocket::Message(source) << "FTOPIC " << c->name << " " << ts << " " << c->topic_setter << " :" << c->topic;
}
}
void SendVhostDel(User *u) override
{
if (u->HasMode("CLOAK"))
this->SendChgHostInternal(u->nick, u->chost);
else
this->SendChgHostInternal(u->nick, u->host);
if (Servers::Capab.count("CHGIDENT") && u->GetIdent() != u->GetVIdent())
this->SendChgIdentInternal(u->nick, u->GetIdent());
}
void SendAkill(User *u, XLine *x) override
{
// Calculate the time left before this would expire, capping it at 2 days
time_t timeleft = x->GetExpires() - Anope::CurTime;
if (timeleft > 172800 || !x->GetExpires())
timeleft = 172800;
/* InspIRCd may support regex bans, if they do we can send this and forget about it */
if (x->IsRegex() && Servers::Capab.count("RLINE"))
{
Anope::string mask = x->GetMask();
size_t h = mask.find('#');
if (h != Anope::string::npos)
mask = mask.replace(h, 1, ' ');
SendAddLine("R", mask, timeleft, x->GetBy(), x->GetReason());
return;
}
else if (x->IsRegex() || x->HasNickOrReal())
{
if (!u)
{
/* No user (this akill was just added), and contains nick and/or realname. Find users that match and ban them */
for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
if (x->GetManager()->Check(it->second, x))
this->SendAkill(it->second, x);
return;
}
XLine *old = x;
if (old->GetManager()->HasEntry("*@" + u->host))
return;
/* We can't akill x as it has a nick and/or realname included, so create a new akill for *@host */
x = Serialize::New<XLine *>();
x->SetMask("*@" + u->host);
x->SetBy(old->GetBy());
x->SetExpires(old->GetExpires());
x->SetReason(old->GetReason());
old->GetManager()->AddXLine(x);
Log(Config->GetClient("OperServ"), "akill") << "AKILL: Added an akill for " << x->GetMask() << " because " << u->GetMask() << "#" << u->realname << " matches " << old->GetMask();
}
/* ZLine if we can instead */
if (x->GetUser() == "*")
{
cidr addr(x->GetHost());
if (addr.valid())
{
IRCD->SendSZLine(u, x);
return;
}
}
SendAddLine("G", x->GetUser() + "@" + x->GetHost(), timeleft, x->GetBy(), x->GetReason());
}
void SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf) override
{
UplinkSocket::Message() << "PUSH " << dest << " ::" << Me->GetName() << " " << numeric << " " << dest << " " << buf;
}
void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) override
{
UplinkSocket::Message(source) << "FMODE " << dest->name << " " << dest->creation_time << " " << buf;
}
void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
UplinkSocket::Message(Me) << "UID " << u->GetUID() << " " << u->timestamp << " " << u->nick << " " << u->host << " " << u->host << " " << u->GetIdent() << " 0.0.0.0 " << u->timestamp << " " << modes << " :" << u->realname;
if (modes.find('o') != Anope::string::npos)
UplinkSocket::Message(u) << "OPERTYPE :services";
}
/* SERVER services-dev.chatspike.net password 0 :Description here */
void SendServer(const Server *server) override
{
/* if rsquit is set then we are waiting on a squit */
if (rsquit_id.empty() && rsquit_server.empty())
UplinkSocket::Message() << "SERVER " << server->GetName() <&
|