/* * Anope IRC Services * * Copyright (C) 2003-2016 Anope Team * * 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 . */ /* Dependencies: anope_protocol.rfc1459 */ #include "module.h" #include "modules/protocol/rfc1459.h" #include "modules/protocol/bahamut.h" class ChannelModeFlood : public ChannelModeParam { public: ChannelModeFlood(char modeChar, bool minusNoArg) : ChannelModeParam("FLOOD", modeChar, minusNoArg) { } bool IsValid(Anope::string &value) const override { try { Anope::string rest; if (!value.empty() && value[0] != ':' && convertTo(value[0] == '*' ? value.substr(1) : value, rest, false) > 0 && rest[0] == ':' && rest.length() > 1 && convertTo(rest.substr(1), rest, false) > 0 && rest.empty()) return true; } catch (const ConvertException &) { } return false; } }; class BahamutIRCdProto : public IRCDProto { public: BahamutIRCdProto(Module *creator) : IRCDProto(creator, "Bahamut 1.8.x") { DefaultPseudoclientModes = "+"; CanSVSNick = true; CanSNLine = true; CanSQLine = true; CanSQLineChannel = true; CanSZLine = true; CanSVSHold = true; MaxModes = 60; } void SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) override { if (Servers::Capab.count("TSMODE") > 0) { IRCMessage message(source, "MODE", dest->name, dest->creation_time); message.TokenizeAndPush(buf); Uplink::SendMessage(message); } else { IRCDProto::SendModeInternal(source, dest, buf); } } void SendModeInternal(const MessageSource &source, User *u, const Anope::string &buf) override { IRCMessage message(source, "SVSMODE", u->nick, u->timestamp); message.TokenizeAndPush(buf); Uplink::SendMessage(message); } 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); } /* SVSHOLD - set */ void SendSVSHold(const Anope::string &nick, time_t time) override { Uplink::Send(Me, "SVSHOLD", nick, time, "Being held for registered user"); } /* SVSHOLD - release */ void SendSVSHoldDel(const Anope::string &nick) override { Uplink::Send(Me, "SVSHOLD", nick, 0); } /* SQLINE */ void SendSQLine(User *, XLine *x) override { Uplink::Send(Me, "SQLINE", x->GetMask(), x->GetReason()); } /* UNSLINE */ void SendSGLineDel(XLine *x) override { Uplink::Send("UNSGLINE", 0, x->GetMask()); } /* UNSZLINE */ void SendSZLineDel(XLine *x) override { /* this will likely fail so its only here for legacy */ Uplink::Send("UNSZLINE", 0, x->GetHost()); /* this is how we are supposed to deal with it */ Uplink::Send("RAKILL", x->GetHost(), "*"); } /* SZLINE */ void SendSZLine(User *, 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; /* this will likely fail so its only here for legacy */ Uplink::Send("SZLINE", x->GetHost(), x->GetReason()); /* this is how we are supposed to deal with it */ Uplink::Send("AKILL", x->GetHost(), "*", timeleft, x->GetBy(), Anope::CurTime, x->GetReason()); } /* SVSNOOP */ void SendSVSNOOP(const Server *server, bool set) override { Uplink::Send("SVSNOOP", server->GetName(), set ? "+" : "-"); } /* SGLINE */ void SendSGLine(User *, XLine *x) override { Uplink::Send("SGLINE", x->GetMask().length(), x->GetMask() + ":" + x->GetReason()); } /* RAKILL */ void SendAkillDel(XLine *x) override { if (x->IsRegex() || x->HasNickOrReal()) return; /* ZLine if we can instead */ if (x->GetUser() == "*") { cidr a(x->GetHost()); if (a.valid()) { IRCD->SendSZLineDel(x); return; } } Uplink::Send("RAKKILL", x->GetHost(), x->GetUser()); } /* TOPIC */ void SendTopic(const MessageSource &source, Channel *c) override { Uplink::Send(source, "TOPIC", c->name, c->topic_setter, c->topic_ts, c->topic); } /* UNSQLINE */ void SendSQLineDel(XLine *x) override { Uplink::Send("UNSQLINE", x->GetMask()); } /* JOIN - SJOIN */ void SendJoin(User *user, Channel *c, const ChannelStatus *status) override { Uplink::Send(user, "SJOIN", c->creation_time, c->name); if (status) { /* First save the channel status incase uc->Status == status */ ChannelStatus cs = *status; /* If the user is internally on the channel with flags, kill them so that * the stacker will allow this. */ ChanUserContainer *uc = c->FindUser(user); if (uc != NULL) uc->status.Clear(); ServiceBot *setter = ServiceBot::Find(user->GetUID()); for (size_t i = 0; i < cs.Modes().length(); ++i) c->SetMode(setter, ModeManager::FindChannelModeByChar(cs.Modes()[i]), user->GetUID(), false); if (uc != NULL) uc->status = cs; } } void SendAkill(User *u, XLine *x) override { 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(); x->SetMask("*@" + u->host); x->SetBy(old->GetBy()); x->SetExpires(old->GetExpires()); x->SetReason(old->GetReason()); x->SetID(old->GetID()); 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 a(x->GetHost()); if (a.valid()) { IRCD->SendSZLine(u, x); return; } } // Calculate the time left before this would expire, capping it at 2 days time_t timeleft = x->GetExpires() - Anope::CurTime; if (timeleft > 172800) timeleft = 172800; Uplink::Send("AKILL", x->GetHost(), x->GetUser(), timeleft, x->GetBy(), Anope::CurTime, x->GetReason()); } /* Note: if the stamp is null 0, the below usage is correct of Bahamut */ void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) override { Uplink::Send(source, "SVSKILL", user->nick, buf); } void SendBOB() override { Uplink::Send("BURST"); } void SendEOB() override { Uplink::Send("BURST", 0); } void SendClientIntroduction(User *u) override { Anope::string modes = "+" + u->GetModes(); Uplink::Send("NICK", u->nick, 1, u->timestamp, modes, u->GetIdent(), u->host, u->server->GetName(), 0, 0, u->realname); } /* SERVER */ void SendServer(const Server *server) override { Uplink::Send("SERVER", server->GetName(), server->GetHops(), server->GetDescription()); } void SendConnect() override { Uplink::Send("PASS", Config->Uplinks[Anope::CurrentUplink].password, "TS"); Uplink::Send("CAPAB", "SSJOIN NOQUIT BURST UNCONNECT NICKIP TSMODE TS3"); SendServer(Me); /* * SVINFO * parv[0] = sender prefix * parv[1] = TS_CURRENT for the server * parv[2] = TS_MIN for the server * parv[3] = server is standalone or connected to non-TS only * parv[4] = server's idea of UTC time */ Uplink::Send("SVINFO", 3, 1, 0, Anope::CurTime); this->SendBOB(); } void SendChannel(Channel *c) override { Anope::string modes = c->GetModes(true, true); if (modes.empty()) modes = "+"; Uplink::Send("SJOIN", c->creation_time, c->name, modes, ""); } void SendLogin(User *u, NickServ::Nick *) override { IRCD->SendMode(Config->GetClient("NickServ"), u, "+d %d", u->signon); } void SendLogout(User *u) override { IRCD->SendMode(Config->GetClient("NickServ"), u, "+d 1"); } }; void bahamut::Burst::Run(MessageSource &source, const std::vector ¶ms) { Server *s = source.GetServer(); s->Sync(true); } void bahamut::Mode::Run(MessageSource &source, const std::vector ¶ms) { if (params.size() > 2 && IRCD->IsChannelValid(params[0])) { Channel *c = Channel::Find(params[0]); time_t ts = 0; try { ts = convertTo(params[1]); } catch (const ConvertException &) { } Anope::string modes = params[2]; for (unsigned int i = 3; i < params.size(); ++i) modes += " " + params[i]; if (c) c->SetModesInternal(source, modes, ts); } else { User *u = User::Find(params[0]); if (u) u->SetModesInternal(source, "%s", params[1].c_str()); } } /* ** NICK - new ** source = NULL ** parv[0] = nickname ** parv[1] = hopcount ** parv[2] = timestamp ** parv[3] = modes ** parv[4] = username ** parv[5] = hostname ** parv[6] = server ** parv[7] = servicestamp ** parv[8] = IP ** parv[9] = info ** NICK - change ** source = oldnick ** parv[0] = new nickname ** parv[1] = hopcount */ void bahamut::Nick::Run(MessageSource &source, const std::vector ¶ms) { if (params.size() == 10) { Server *s = Server::Find(params[6]); if (s == nullptr) { Log(LOG_DEBUG) << "User " << params[0] << " introduced from non-existent server " << params[6] << "?"; return; } NickServ::Nick *na = nullptr; time_t signon = 0, stamp = 0; try { signon = convertTo(params[2]); stamp = convertTo(params[7]); } catch (const ConvertException &) { } if (signon && signon == stamp && NickServ::service) na = NickServ::service->FindNick(params[0]); User::OnIntroduce(params[0], params[4], params[5], "", params[8], s, params[9], signon, params[3], "", na ? na->GetAccount() : nullptr); } else { User *u = source.GetUser(); if (u) u->ChangeNick(params[0]); } } void bahamut::ServerMessage::Run(MessageSource &source, const std::vector ¶ms) { unsigned int hops = 0; try { hops = convertTo(params[1]); } catch (const ConvertException &) { } new Server(source.GetServer(), params[0], hops, params[2]); } void bahamut::SJoin::Run(MessageSource &source, const std::vector ¶ms) { Anope::string modes; if (params.size() >= 4) for (unsigned i = 2; i < params.size(); ++i) modes += " " + params[i]; if (!modes.empty()) modes.erase(modes.begin()); std::list users; /* For some reason, bahamut will send a SJOIN from the user joining a channel * if the channel already existed */ if (source.GetUser()) { rfc1459::Join::SJoinUser sju; sju.second = source.GetUser(); users.push_back(sju); } else { spacesepstream sep(params[params.size() - 1]); Anope::string buf; while (sep.GetToken(buf)) { rfc1459::Join::SJoinUser sju; /* Get prefixes from the nick */ for (char ch; !buf.empty() && (ch = ModeManager::GetStatusChar(buf[0]));) { buf.erase(buf.begin()); sju.first.AddMode(ch); } sju.second = User::Find(buf); if (!sju.second) { Log(LOG_DEBUG) << "SJOIN for non-existent user " << buf << " on " << params[1]; continue; } users.push_back(sju); } } time_t ts = Anope::CurTime; try { ts = convertTo(params[0]); } catch (const ConvertException &) { } rfc1459::Join::SJoin(source, params[1], ts, modes, users); } void bahamut::Topic::Run(MessageSource &source, const std::vector ¶ms) { Channel *c = Channel::Find(params[0]); time_t ts = Anope::CurTime; try { ts = convertTo(params[2]); } catch (const ConvertException &) { } if (c) c->ChangeTopicInternal(source.GetUser(), params[1], params[3], ts); } class ProtoBahamut : public Module , public EventHook { BahamutIRCdProto ircd_proto; /* Core message handlers */ rfc1459::Away message_away; rfc1459::Capab message_capab; rfc1459::Error message_error; rfc1459::Invite message_invite; rfc1459::Join message_join; rfc1459::Kick message_kick; rfc1459::Kill message_kill; rfc1459::MOTD message_motd; rfc1459::Notice message_notice; rfc1459::Part message_part; rfc1459::Ping message_ping; rfc1459::Privmsg message_privmsg; rfc1459::Quit message_quit; rfc1459::SQuit message_squit; rfc1459::Stats message_stats; rfc1459::Time message_time; rfc1459::Version message_version; rfc1459::Whois message_whois; /* Our message handlers */ bahamut::Burst message_burst; bahamut::Mode message_mode, message_svsmode; bahamut::Nick message_nick; bahamut::ServerMessage message_server; bahamut::SJoin message_sjoin; bahamut::Topic message_topic; public: ProtoBahamut(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PROTOCOL | VENDOR) , EventHook(this) , ircd_proto(this) , message_away(this) , message_capab(this) , message_error(this) , message_invite(this) , message_join(this) , message_kick(this) , message_kill(this) , message_motd(this) , message_notice(this) , message_part(this) , message_ping(this) , message_privmsg(this) , message_quit(this) , message_squit(this) , message_stats(this) , message_time(this) , message_version(this) , message_whois(this) , message_burst(this) , message_mode(this, "MODE") , message_svsmode(this, "SVSMODE") , message_nick(this) , message_server(this) , message_sjoin(this) , message_topic(this) { } void OnUserNickChange(User *u, const Anope::string &) override { u->RemoveModeInternal(Me, ModeManager::FindUserModeByName("REGISTERED")); IRCD->SendLogout(u); } }; MODULE_INIT(ProtoBahamut)