/*
* 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
Uplink::Send(Me, "CHGIDENT", nick, vIdent);
}
void SendChgHostInternal(const Anope::string &nick, const Anope::string &vhost)
{
if (!Servers::Capab.count("CHGHOST"))
Log() << "CHGHOST not loaded!";
else
Uplink::Send(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)
{
Uplink::Send(Me, "ADDLINE", xtype, mask, addedby, Anope::CurTime, duration, reason);
}
void SendDelLine(const Anope::string &xtype, const Anope::string &mask)
{
Uplink::Send(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
{
Uplink::Send("CAPAB START 1202");
Uplink::Send("CAPAB CAPABILITIES :PROTOCOL=1202");
Uplink::Send("CAPAB END");
SendServer(Me);
}
void SendGlobalNotice(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
Uplink::Send(bi, "NOTICE", "$" + dest->GetName(), msg);
}
void SendGlobalPrivmsg(ServiceBot *bi, const Server *dest, const Anope::string &msg) override
{
Uplink::Send(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"))
{
Uplink::Send(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 */
Uplink::Send(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
{
User *u = User::Find(dest);
Uplink::Send("PUSH", dest, ":" + Me->GetName() + " " + numeric + " " + (u ? u->nick : dest) + " " + buf);
}
void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) override
{
IRCMessage message(source, "FMODE", dest->name, dest->creation_time);
message.TokenizeAndPush(buf);
Uplink::SendMessage(message);
}
void SendClientIntroduction(User *u) override
{
Anope::string modes = "+" + u->GetModes();
Uplink::Send(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)
Uplink::Send(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())
Uplink::Send("SERVER", server->GetName(), Config->Uplinks[Anope::CurrentUplink].password, server->GetHops(), server->GetSID(), server->GetDescription());
}
void SendSquit(Server *s, const Anope::string &message) override
{
if (s != Me)
{
rsquit_id = s->GetSID();
rsquit_server = s->GetName()
|