X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree%2Fmain.cpp;h=fbc6ad40c8059d88493b747b60e7377867c6ec63;hb=687778b72e31322a73b2e2e17af6bd0f2a2561bc;hp=bfd3f75eae34177a488ce8f3001e4ff90bbf17ef;hpb=23e8bba13c55d33ce89d505780da36c9589e300a;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree/main.cpp b/src/modules/m_spanningtree/main.cpp index bfd3f75ea..fbc6ad40c 100644 --- a/src/modules/m_spanningtree/main.cpp +++ b/src/modules/m_spanningtree/main.cpp @@ -21,11 +21,10 @@ */ -/* $ModDesc: Provides a spanning tree server link protocol */ - #include "inspircd.h" #include "socket.h" #include "xline.h" +#include "iohook.h" #include "resolvers.h" #include "main.h" @@ -34,59 +33,77 @@ #include "link.h" #include "treesocket.h" #include "commands.h" -#include "protocolinterface.h" +#include "translate.h" ModuleSpanningTree::ModuleSpanningTree() - : commands(NULL), DNS(this, "DNS"), Utils(NULL) + : Away::EventListener(this) + , Stats::EventListener(this) + , CTCTags::EventListener(this) + , rconnect(this) + , rsquit(this) + , map(this) + , commands(this) + , currmembid(0) + , broadcasteventprov(this, "event/server-broadcast") + , linkeventprov(this, "event/server-link") + , messageeventprov(this, "event/server-message") + , synceventprov(this, "event/server-sync") + , sslapi(this) + , DNS(this, "DNS") + , tagevprov(this, "event/messagetag") + , loopCall(false) { } SpanningTreeCommands::SpanningTreeCommands(ModuleSpanningTree* module) - : rconnect(module, module->Utils), rsquit(module, module->Utils), - svsjoin(module), svspart(module), svsnick(module), metadata(module), + : svsjoin(module), svspart(module), svsnick(module), metadata(module), uid(module), opertype(module), fjoin(module), ijoin(module), resync(module), - fmode(module), ftopic(module), fhost(module), fident(module), fname(module) + fmode(module), ftopic(module), fhost(module), fident(module), fname(module), + away(module), addline(module), delline(module), encap(module), idle(module), + nick(module), ping(module), pong(module), save(module), + server(module), squit(module), snonotice(module), + endburst(module), sinfo(module), num(module) +{ +} + +namespace { + void SetLocalUsersServer(Server* newserver) + { + // Does not change the server of quitting users because those are not in the list + + ServerInstance->FakeClient->server = newserver; + const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers(); + for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i) + (*i)->server = newserver; + } + + void ResetMembershipIds() + { + // Set all membership ids to 0 + const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers(); + for (UserManager::LocalList::iterator i = list.begin(); i != list.end(); ++i) + { + LocalUser* user = *i; + for (User::ChanList::iterator j = user->chans.begin(); j != user->chans.end(); ++j) + (*j)->id = 0; + } + } } void ModuleSpanningTree::init() { + ServerInstance->SNO->EnableSnomask('l', "LINK"); + + ResetMembershipIds(); + Utils = new SpanningTreeUtilities(this); - commands = new SpanningTreeCommands(this); - ServerInstance->Modules->AddService(commands->rconnect); - ServerInstance->Modules->AddService(commands->rsquit); - ServerInstance->Modules->AddService(commands->svsjoin); - ServerInstance->Modules->AddService(commands->svspart); - ServerInstance->Modules->AddService(commands->svsnick); - ServerInstance->Modules->AddService(commands->metadata); - ServerInstance->Modules->AddService(commands->uid); - ServerInstance->Modules->AddService(commands->opertype); - ServerInstance->Modules->AddService(commands->fjoin); - ServerInstance->Modules->AddService(commands->ijoin); - ServerInstance->Modules->AddService(commands->resync); - ServerInstance->Modules->AddService(commands->fmode); - ServerInstance->Modules->AddService(commands->ftopic); - ServerInstance->Modules->AddService(commands->fhost); - ServerInstance->Modules->AddService(commands->fident); - ServerInstance->Modules->AddService(commands->fname); - - Implementation eventlist[] = - { - I_OnPreCommand, I_OnGetServerDescription, I_OnUserInvite, I_OnPostTopicChange, - I_OnWallops, I_OnUserNotice, I_OnUserMessage, I_OnBackgroundTimer, I_OnUserJoin, - I_OnChangeHost, I_OnChangeName, I_OnChangeIdent, I_OnUserPart, I_OnUnloadModule, - I_OnUserQuit, I_OnUserPostNick, I_OnUserKick, I_OnRemoteKill, I_OnRehash, I_OnPreRehash, - I_OnOper, I_OnAddLine, I_OnDelLine, I_OnMode, I_OnLoadModule, I_OnStats, - I_OnSetAway, I_OnPostCommand, I_OnUserConnect, I_OnAcceptConnection - }; - ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation)); - - delete ServerInstance->PI; - ServerInstance->PI = new SpanningTreeProtocolInterface(Utils); - loopCall = false; - - // update our local user count - Utils->TreeRoot->UserCount = ServerInstance->Users->local_users.size(); + Utils->TreeRoot = new TreeServer; + + ServerInstance->PI = &protocolinterface; + + delete ServerInstance->FakeClient->server; + SetLocalUsersServer(Utils->TreeRoot); } void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops) @@ -96,130 +113,39 @@ void ModuleSpanningTree::ShowLinks(TreeServer* Current, User* user, int hops) { Parent = Current->GetParent()->GetName(); } - for (unsigned int q = 0; q < Current->ChildCount(); q++) + + const TreeServer::ChildServers& children = Current->GetChildren(); + for (TreeServer::ChildServers::const_iterator i = children.begin(); i != children.end(); ++i) { - if ((Current->GetChild(q)->Hidden) || ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName())))) + TreeServer* server = *i; + if ((server->Hidden) || ((Utils->HideULines) && (server->IsULine()))) { if (user->IsOper()) { - ShowLinks(Current->GetChild(q),user,hops+1); + ShowLinks(server, user, hops+1); } } else { - ShowLinks(Current->GetChild(q),user,hops+1); + ShowLinks(server, user, hops+1); } } /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */ - if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName())) && (!user->IsOper())) + if ((Utils->HideULines) && (Current->IsULine()) && (!user->IsOper())) return; /* Or if the server is hidden and they're not an oper */ else if ((Current->Hidden) && (!user->IsOper())) return; - std::string servername = Current->GetName(); - user->WriteNumeric(364, "%s %s %s :%d %s", user->nick.c_str(), servername.c_str(), - (Utils->FlatLinks && (!user->IsOper())) ? ServerInstance->Config->ServerName.c_str() : Parent.c_str(), - (Utils->FlatLinks && (!user->IsOper())) ? 0 : hops, - Current->GetDesc().c_str()); -} - -int ModuleSpanningTree::CountServs() -{ - return Utils->serverlist.size(); + user->WriteNumeric(RPL_LINKS, Current->GetName(), + (((Utils->FlatLinks) && (!user->IsOper())) ? ServerInstance->Config->ServerName : Parent), + InspIRCd::Format("%d %s", (((Utils->FlatLinks) && (!user->IsOper())) ? 0 : hops), Current->GetDesc().c_str())); } -void ModuleSpanningTree::HandleLinks(const std::vector& parameters, User* user) +void ModuleSpanningTree::HandleLinks(const CommandBase::Params& parameters, User* user) { ShowLinks(Utils->TreeRoot,user,0); - user->WriteNumeric(365, "%s * :End of /LINKS list.",user->nick.c_str()); - return; -} - -std::string ModuleSpanningTree::TimeToStr(time_t secs) -{ - time_t mins_up = secs / 60; - time_t hours_up = mins_up / 60; - time_t days_up = hours_up / 24; - secs = secs % 60; - mins_up = mins_up % 60; - hours_up = hours_up % 24; - return ((days_up ? (ConvToStr(days_up) + "d") : "") - + (hours_up ? (ConvToStr(hours_up) + "h") : "") - + (mins_up ? (ConvToStr(mins_up) + "m") : "") - + ConvToStr(secs) + "s"); -} - -void ModuleSpanningTree::DoPingChecks(time_t curtime) -{ - /* - * Cancel remote burst mode on any servers which still have it enabled due to latency/lack of data. - * This prevents lost REMOTECONNECT notices - */ - long ts = ServerInstance->Time() * 1000 + (ServerInstance->Time_ns() / 1000000); - -restart: - for (server_hash::iterator i = Utils->serverlist.begin(); i != Utils->serverlist.end(); i++) - { - TreeServer *s = i->second; - - if (s->GetSocket() && s->GetSocket()->GetLinkState() == DYING) - { - s->GetSocket()->Close(); - goto restart; - } - - // Fix for bug #792, do not ping servers that are not connected yet! - // Remote servers have Socket == NULL and local connected servers have - // Socket->LinkState == CONNECTED - if (s->GetSocket() && s->GetSocket()->GetLinkState() != CONNECTED) - continue; - - // Now do PING checks on all servers - TreeServer *mts = Utils->BestRouteTo(s->GetID()); - - if (mts) - { - // Only ping if this server needs one - if (curtime >= s->NextPingTime()) - { - // And if they answered the last - if (s->AnsweredLastPing()) - { - // They did, send a ping to them - s->SetNextPingTime(curtime + Utils->PingFreq); - TreeSocket *tsock = mts->GetSocket(); - - // ... if we can find a proper route to them - if (tsock) - { - tsock->WriteLine(":" + ServerInstance->Config->GetSID() + " PING " + s->GetID()); - s->LastPingMsec = ts; - } - } - else - { - // They didn't answer the last ping, if they are locally connected, get rid of them. - TreeSocket *sock = s->GetSocket(); - if (sock) - { - sock->SendError("Ping timeout"); - sock->Close(); - goto restart; - } - } - } - - // If warn on ping enabled and not warned and the difference is sufficient and they didn't answer the last ping... - if ((Utils->PingWarnTime) && (!s->Warned) && (curtime >= s->NextPingTime() - (Utils->PingFreq - Utils->PingWarnTime)) && (!s->AnsweredLastPing())) - { - /* The server hasnt responded, send a warning to opers */ - std::string servername = s->GetName(); - ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 has not responded to PING for %d seconds, high latency.", servername.c_str(), Utils->PingWarnTime); - s->Warned = true; - } - } - } + user->WriteNumeric(RPL_ENDOFLINKS, '*', "End of /LINKS list."); } void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer) @@ -261,33 +187,35 @@ void ModuleSpanningTree::ConnectServer(Autoconnect* a, bool on_timer) void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) { - bool ipvalid = true; - - if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name))) + if (InspIRCd::Match(ServerInstance->Config->ServerName, x->Name, ascii_case_insensitive_map)) { ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Not connecting to myself."); return; } - DNS::QueryType start_type = DNS::QUERY_AAAA; - if (strchr(x->IPAddr.c_str(),':')) + irc::sockets::sockaddrs sa; + if (x->IPAddr.find('/') != std::string::npos) { - in6_addr n; - if (inet_pton(AF_INET6, x->IPAddr.c_str(), &n) < 1) - ipvalid = false; + if (!irc::sockets::isunix(x->IPAddr) || !irc::sockets::untosa(x->IPAddr, sa)) + { + // We don't use the family() != AF_UNSPEC check below for UNIX sockets as + // that results in a DNS lookup. + ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s is not a UNIX socket!", + x->Name.c_str(), x->IPAddr.c_str()); + return; + } } else { - in_addr n; - if (inet_aton(x->IPAddr.c_str(),&n) < 1) - ipvalid = false; + // If this fails then the IP sa will be AF_UNSPEC. + irc::sockets::aptosa(x->IPAddr, x->Port, sa); } - + /* Do we already have an IP? If so, no need to resolve it. */ - if (ipvalid) + if (sa.family() != AF_UNSPEC) { - /* Gave a hook, but it wasnt one we know */ - TreeSocket* newsocket = new TreeSocket(Utils, x, y, x->IPAddr); + // Create a TreeServer object that will start connecting immediately in the background + TreeSocket* newsocket = new TreeSocket(x, y, sa); if (newsocket->GetFd() > -1) { /* Handled automatically on success */ @@ -301,11 +229,20 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) } else if (!DNS) { - ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and m_dns.so is not loaded, unable to resolve.", x->Name.c_str()); + ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: Hostname given and core_dns is not loaded, unable to resolve.", x->Name.c_str()); } else { - ServernameResolver* snr = new ServernameResolver(Utils, *DNS, x->IPAddr, x, start_type, y); + // Guess start_type from bindip aftype + DNS::QueryType start_type = DNS::QUERY_AAAA; + irc::sockets::sockaddrs bind; + if ((!x->Bind.empty()) && (irc::sockets::aptosa(x->Bind, 0, bind))) + { + if (bind.family() == AF_INET) + start_type = DNS::QUERY_A; + } + + ServernameResolver* snr = new ServernameResolver(*DNS, x->IPAddr, x, start_type, y); try { DNS->Process(snr); @@ -313,7 +250,7 @@ void ModuleSpanningTree::ConnectServer(Link* x, Autoconnect* y) catch (DNS::Exception& e) { delete snr; - ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason()); + ServerInstance->SNO->WriteToSnoMask('l', "CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason().c_str()); ConnectServer(y, false); } } @@ -334,12 +271,12 @@ void ModuleSpanningTree::AutoConnectServers(time_t curtime) void ModuleSpanningTree::DoConnectTimeout(time_t curtime) { - std::map >::iterator i = Utils->timeoutlist.begin(); + SpanningTreeUtilities::TimeoutList::iterator i = Utils->timeoutlist.begin(); while (i != Utils->timeoutlist.end()) { TreeSocket* s = i->first; - std::pair p = i->second; - std::map >::iterator me = i; + std::pair p = i->second; + SpanningTreeUtilities::TimeoutList::iterator me = i; i++; if (s->GetLinkState() == DYING) { @@ -348,16 +285,16 @@ void ModuleSpanningTree::DoConnectTimeout(time_t curtime) } else if (curtime > s->age + p.second) { - ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %d seconds)",p.first.c_str(),p.second); + ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002 (timeout of %u seconds)",p.first.c_str(),p.second); Utils->timeoutlist.erase(me); s->Close(); } } } -ModResult ModuleSpanningTree::HandleVersion(const std::vector& parameters, User* user) +ModResult ModuleSpanningTree::HandleVersion(const CommandBase::Params& parameters, User* user) { - // we've already checked if pcnt > 0, so this is safe + // We've already confirmed that !parameters.empty(), so this is safe TreeServer* found = Utils->FindServerMask(parameters[0]); if (found) { @@ -366,84 +303,80 @@ ModResult ModuleSpanningTree::HandleVersion(const std::vector& para // Pass to default VERSION handler. return MOD_RES_PASSTHRU; } - std::string Version = found->GetVersion(); - user->WriteNumeric(351, "%s :%s",user->nick.c_str(),Version.c_str()); + + // If an oper wants to see the version then show the full version string instead of the normal, + // but only if it is non-empty. + // If it's empty it might be that the server is still syncing (full version hasn't arrived yet) + // or the server is a 2.0 server and does not send a full version. + bool showfull = ((user->IsOper()) && (!found->GetFullVersion().empty())); + + Numeric::Numeric numeric(RPL_VERSION); + irc::tokenstream tokens(showfull ? found->GetFullVersion() : found->GetVersion()); + for (std::string token; tokens.GetTrailing(token); ) + numeric.push(token); + user->WriteNumeric(numeric); } else { - user->WriteNumeric(402, "%s %s :No such server",user->nick.c_str(),parameters[0].c_str()); + user->WriteNumeric(ERR_NOSUCHSERVER, parameters[0], "No such server"); } return MOD_RES_DENY; } -/* This method will attempt to get a message to a remote user. - */ -void ModuleSpanningTree::RemoteMessage(User* user, const char* format, ...) -{ - char text[MAXBUF]; - va_list argsPtr; - - va_start(argsPtr, format); - vsnprintf(text, MAXBUF, format, argsPtr); - va_end(argsPtr); - - if (IS_LOCAL(user)) - user->WriteNotice(text); - else - ServerInstance->PI->SendUserNotice(user, text); -} - -ModResult ModuleSpanningTree::HandleConnect(const std::vector& parameters, User* user) +ModResult ModuleSpanningTree::HandleConnect(const CommandBase::Params& parameters, User* user) { for (std::vector >::iterator i = Utils->LinkBlocks.begin(); i < Utils->LinkBlocks.end(); i++) { Link* x = *i; - if (InspIRCd::Match(x->Name.c_str(),parameters[0])) + if (InspIRCd::Match(x->Name, parameters[0], ascii_case_insensitive_map)) { - if (InspIRCd::Match(ServerInstance->Config->ServerName, assign(x->Name))) + if (InspIRCd::Match(ServerInstance->Config->ServerName, x->Name, ascii_case_insensitive_map)) { - RemoteMessage(user, "*** CONNECT: Server \002%s\002 is ME, not connecting.",x->Name.c_str()); + user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Server \002%s\002 is ME, not connecting.", x->Name.c_str())); return MOD_RES_DENY; } - TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str()); + TreeServer* CheckDupe = Utils->FindServer(x->Name); if (!CheckDupe) { - RemoteMessage(user, "*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",x->Name.c_str(),(x->HiddenFromStats ? "" : x->IPAddr.c_str()),x->Port); + user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Connecting to server: \002%s\002 (%s:%d)", x->Name.c_str(), (x->HiddenFromStats ? "" : x->IPAddr.c_str()), x->Port)); ConnectServer(x); return MOD_RES_DENY; } else { - std::string servername = CheckDupe->GetParent()->GetName(); - RemoteMessage(user, "*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), servername.c_str()); + user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002", x->Name.c_str(), CheckDupe->GetParent()->GetName().c_str())); return MOD_RES_DENY; } } } - RemoteMessage(user, "*** CONNECT: No server matching \002%s\002 could be found in the config file.",parameters[0].c_str()); + user->WriteRemoteNotice(InspIRCd::Format("*** CONNECT: No server matching \002%s\002 could be found in the config file.", parameters[0].c_str())); return MOD_RES_DENY; } -void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description) +void ModuleSpanningTree::OnUserInvite(User* source, User* dest, Channel* channel, time_t expiry, unsigned int notifyrank, CUList& notifyexcepts) { - TreeServer* s = Utils->FindServer(servername); - if (s) + if (IS_LOCAL(source)) { - description = s->GetDesc(); + CmdBuilder params(source, "INVITE"); + params.push(dest->uuid); + params.push(channel->name); + params.push_int(channel->age); + params.push(ConvToStr(expiry)); + params.Broadcast(); } } -void ModuleSpanningTree::OnUserInvite(User* source,User* dest,Channel* channel, time_t expiry) +ModResult ModuleSpanningTree::OnPreTopicChange(User* user, Channel* chan, const std::string& topic) { - if (IS_LOCAL(source)) + // XXX: Deny topic changes if the current topic set time is the current time or is in the future because + // other servers will drop our FTOPIC. This restriction will be removed when the protocol is updated. + if ((chan->topicset >= ServerInstance->Time()) && (Utils->serverlist.size() > 1)) { - parameterlist params; - params.push_back(dest->uuid); - params.push_back(channel->name); - params.push_back(ConvToStr(expiry)); - Utils->DoOneToMany(source->uuid,"INVITE",params); + user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, "Retry topic change later"); + return MOD_RES_DENY; } + return MOD_RES_PASSTHRU; } void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std::string &topic) @@ -452,67 +385,87 @@ void ModuleSpanningTree::OnPostTopicChange(User* user, Channel* chan, const std: if (!IS_LOCAL(user)) return; - parameterlist params; - params.push_back(chan->name); - params.push_back(":"+topic); - Utils->DoOneToMany(user->uuid,"TOPIC",params); + CommandFTopic::Builder(user, chan).Broadcast(); } -void ModuleSpanningTree::OnWallops(User* user, const std::string &text) +void ModuleSpanningTree::OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) { - if (IS_LOCAL(user)) + if (!IS_LOCAL(user)) + return; + + const char* message_type = (details.type == MSG_PRIVMSG ? "PRIVMSG" : "NOTICE"); + switch (target.type) { - parameterlist params; - params.push_back(":"+text); - Utils->DoOneToMany(user->uuid,"WALLOPS",params); + case MessageTarget::TYPE_USER: + { + User* d = target.Get(); + if (!IS_LOCAL(d)) + { + CmdBuilder params(user, message_type); + params.push_tags(details.tags_out); + params.push(d->uuid); + params.push_last(details.text); + params.Unicast(d); + } + break; + } + case MessageTarget::TYPE_CHANNEL: + { + Utils->SendChannelMessage(user, target.Get(), details.text, target.status, details.tags_out, details.exemptions, message_type); + break; + } + case MessageTarget::TYPE_SERVER: + { + const std::string* serverglob = target.Get(); + CmdBuilder par(user, message_type); + par.push_tags(details.tags_out); + par.push(*serverglob); + par.push_last(details.text); + par.Broadcast(); + break; + } } } -void ModuleSpanningTree::LocalMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list, const char* message_type) +void ModuleSpanningTree::OnUserPostTagMessage(User* user, const MessageTarget& target, const CTCTags::TagMessageDetails& details) { - /* Server or remote origin, dest should always be non-null */ - if ((!user) || (!IS_LOCAL(user)) || (!dest)) + if (!IS_LOCAL(user)) return; - if (target_type == TYPE_USER) + switch (target.type) { - User* d = (User*) dest; - if (!IS_LOCAL(d)) + case MessageTarget::TYPE_USER: { - parameterlist params; - params.push_back(d->uuid); - params.push_back(":"+text); - Utils->DoOneToOne(user->uuid, message_type, params, d->server); + User* d = target.Get(); + if (!IS_LOCAL(d)) + { + CmdBuilder params(user, "TAGMSG"); + params.push_tags(details.tags_out); + params.push(d->uuid); + params.Unicast(d); + } + break; + } + case MessageTarget::TYPE_CHANNEL: + { + Utils->SendChannelMessage(user, target.Get(), "", target.status, details.tags_out, details.exemptions, "TAGMSG"); + break; + } + case MessageTarget::TYPE_SERVER: + { + const std::string* serverglob = target.Get(); + CmdBuilder par(user, "TAGMSG"); + par.push_tags(details.tags_out); + par.push(*serverglob); + par.Broadcast(); + break; } } - else if (target_type == TYPE_CHANNEL) - { - Utils->SendChannelMessage(user->uuid, (Channel*)dest, text, status, exempt_list, message_type); - } - else if (target_type == TYPE_SERVER) - { - char* target = (char*) dest; - parameterlist par; - par.push_back(target); - par.push_back(":"+text); - Utils->DoOneToMany(user->uuid, message_type, par); - } -} - -void ModuleSpanningTree::OnUserNotice(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) -{ - LocalMessage(user, dest, target_type, text, status, exempt_list, "NOTICE"); -} - -void ModuleSpanningTree::OnUserMessage(User* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) -{ - LocalMessage(user, dest, target_type, text, status, exempt_list, "PRIVMSG"); } void ModuleSpanningTree::OnBackgroundTimer(time_t curtime) { AutoConnectServers(curtime); - DoPingChecks(curtime); DoConnectTimeout(curtime); } @@ -521,30 +474,19 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user) if (user->quitting) return; - parameterlist params; - params.push_back(user->uuid); - params.push_back(ConvToStr(user->age)); - params.push_back(user->nick); - params.push_back(user->host); - params.push_back(user->dhost); - params.push_back(user->ident); - params.push_back(user->GetIPString()); - params.push_back(ConvToStr(user->signon)); - params.push_back("+"+std::string(user->FormatModes(true))); - params.push_back(":"+user->fullname); - Utils->DoOneToMany(ServerInstance->Config->GetSID(), "UID", params); + // Create the lazy ssl_cert metadata for this user if not already created. + if (sslapi) + sslapi->GetCertificate(user); + + CommandUID::Builder(user).Broadcast(); if (user->IsOper()) - { - params.clear(); - params.push_back(user->oper->name); - Utils->DoOneToMany(user->uuid,"OPERTYPE",params); - } + CommandOpertype::Builder(user).Broadcast(); for(Extensible::ExtensibleStore::const_iterator i = user->GetExtList().begin(); i != user->GetExtList().end(); i++) { ExtensionItem* item = i->first; - std::string value = item->serialize(FORMAT_NETWORK, user, i->second); + std::string value = item->ToNetwork(user, i->second); if (!value.empty()) ServerInstance->PI->SendMetaData(user, item->name, value); } @@ -555,26 +497,30 @@ void ModuleSpanningTree::OnUserConnect(LocalUser* user) void ModuleSpanningTree::OnUserJoin(Membership* memb, bool sync, bool created_by_local, CUList& excepts) { // Only do this for local users - if (IS_LOCAL(memb->user)) + if (!IS_LOCAL(memb->user)) + return; + + // Assign the current membership id to the new Membership and increase it + memb->id = currmembid++; + + if (created_by_local) { - parameterlist params; - params.push_back(memb->chan->name); - if (created_by_local) - { - params.push_back(ConvToStr(memb->chan->age)); - params.push_back(std::string("+") + memb->chan->ChanModes(true)); - params.push_back(memb->modes+","+memb->user->uuid); - Utils->DoOneToMany(ServerInstance->Config->GetSID(),"FJOIN",params); - } - else + CommandFJoin::Builder params(memb->chan); + params.add(memb); + params.finalize(); + params.Broadcast(); + } + else + { + CmdBuilder params(memb->user, "IJOIN"); + params.push(memb->chan->name); + params.push_int(memb->id); + if (!memb->modes.empty()) { - if (!memb->modes.empty()) - { - params.push_back(ConvToStr(memb->chan->age)); - params.push_back(memb->modes); - } - Utils->DoOneToMany(memb->user->uuid, "IJOIN", params); + params.push(ConvToStr(memb->chan->age)); + params.push(memb->modes); } + params.Broadcast(); } } @@ -583,141 +529,120 @@ void ModuleSpanningTree::OnChangeHost(User* user, const std::string &newhost) if (user->registered != REG_ALL || !IS_LOCAL(user)) return; - parameterlist params; - params.push_back(newhost); - Utils->DoOneToMany(user->uuid,"FHOST",params); + CmdBuilder(user, "FHOST").push(newhost).Broadcast(); } -void ModuleSpanningTree::OnChangeName(User* user, const std::string &gecos) +void ModuleSpanningTree::OnChangeRealName(User* user, const std::string& real) { if (user->registered != REG_ALL || !IS_LOCAL(user)) return; - parameterlist params; - params.push_back(gecos); - Utils->DoOneToMany(user->uuid,"FNAME",params); + CmdBuilder(user, "FNAME").push_last(real).Broadcast(); } void ModuleSpanningTree::OnChangeIdent(User* user, const std::string &ident) { - // only occurs for local clients - if (user->registered != REG_ALL) + if ((user->registered != REG_ALL) || (!IS_LOCAL(user))) return; - parameterlist params; - params.push_back(ident); - Utils->DoOneToMany(user->uuid,"FIDENT",params); + CmdBuilder(user, "FIDENT").push(ident).Broadcast(); } void ModuleSpanningTree::OnUserPart(Membership* memb, std::string &partmessage, CUList& excepts) { if (IS_LOCAL(memb->user)) { - parameterlist params; - params.push_back(memb->chan->name); + CmdBuilder params(memb->user, "PART"); + params.push(memb->chan->name); if (!partmessage.empty()) - params.push_back(":"+partmessage); - Utils->DoOneToMany(memb->user->uuid,"PART",params); + params.push_last(partmessage); + params.Broadcast(); } } void ModuleSpanningTree::OnUserQuit(User* user, const std::string &reason, const std::string &oper_message) { - if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) + if (IS_LOCAL(user)) { - parameterlist params; - if (oper_message != reason) + ServerInstance->PI->SendMetaData(user, "operquit", oper_message); + + CmdBuilder(user, "QUIT").push_last(reason).Broadcast(); + } + else + { + // Hide the message if one of the following is true: + // - User is being quit due to a netsplit and quietbursts is on + // - Server is a silent uline + TreeServer* server = TreeServer::Get(user); + bool hide = (((server->IsDead()) && (Utils->quiet_bursts)) || (server->IsSilentULine())); + if (!hide) { - params.push_back(":"+oper_message); - Utils->DoOneToMany(user->uuid,"OPERQUIT",params); + ServerInstance->SNO->WriteToSnoMask('Q', "Client exiting on server %s: %s (%s) [%s]", + user->server->GetName().c_str(), user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_message.c_str()); } - params.clear(); - params.push_back(":"+reason); - Utils->DoOneToMany(user->uuid,"QUIT",params); } - // Regardless, We need to modify the user Counts.. - TreeServer* SourceServer = Utils->FindServer(user->server); - if (SourceServer) - { - SourceServer->UserCount--; - } + // Regardless, update the UserCount + TreeServer::Get(user)->UserCount--; } void ModuleSpanningTree::OnUserPostNick(User* user, const std::string &oldnick) { if (IS_LOCAL(user)) { - parameterlist params; - params.push_back(user->nick); - - /** IMPORTANT: We don't update the TS if the oldnick is just a case change of the newnick! - */ - if (irc::string(user->nick.c_str()) != assign(oldnick)) - user->age = ServerInstance->Time(); - - params.push_back(ConvToStr(user->age)); - Utils->DoOneToMany(user->uuid,"NICK",params); + // The nick TS is updated by the core, we don't do it + CmdBuilder params(user, "NICK"); + params.push(user->nick); + params.push(ConvToStr(user->age)); + params.Broadcast(); } - else if (!loopCall && user->nick == user->uuid) + else if (!loopCall) { - parameterlist params; - params.push_back(user->uuid); - params.push_back(ConvToStr(user->age)); - Utils->DoOneToMany(ServerInstance->Config->GetSID(),"SAVE",params); + ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "WARNING: Changed nick of remote user %s from %s to %s TS %lu by ourselves!", user->uuid.c_str(), oldnick.c_str(), user->nick.c_str(), (unsigned long) user->age); } } void ModuleSpanningTree::OnUserKick(User* source, Membership* memb, const std::string &reason, CUList& excepts) { - parameterlist params; - params.push_back(memb->chan->name); - params.push_back(memb->user->uuid); - params.push_back(":"+reason); - if (IS_LOCAL(source)) - { - Utils->DoOneToMany(source->uuid,"KICK",params); - } - else if (source == ServerInstance->FakeClient) - { - Utils->DoOneToMany(ServerInstance->Config->GetSID(),"KICK",params); - } -} - -void ModuleSpanningTree::OnRemoteKill(User* source, User* dest, const std::string &reason, const std::string &operreason) -{ - if (!IS_LOCAL(source)) - return; // Only start routing if we're origin. + if ((!IS_LOCAL(source)) && (source != ServerInstance->FakeClient)) + return; - ServerInstance->OperQuit.set(dest, operreason); - parameterlist params; - params.push_back(":"+operreason); - Utils->DoOneToMany(dest->uuid,"OPERQUIT",params); - params.clear(); - params.push_back(dest->uuid); - params.push_back(":"+reason); - Utils->DoOneToMany(source->uuid,"KILL",params); + CmdBuilder params(source, "KICK"); + params.push(memb->chan->name); + params.push(memb->user->uuid); + // If a remote user is being kicked by us then send the membership id in the kick too + if (!IS_LOCAL(memb->user)) + params.push_int(memb->id); + params.push_last(reason); + params.Broadcast(); } void ModuleSpanningTree::OnPreRehash(User* user, const std::string ¶meter) { - if (loopCall) - return; // Don't generate a REHASH here if we're in the middle of processing a message that generated this one - - ServerInstance->Logs->Log("remoterehash", LOG_DEBUG, "called with param %s", parameter.c_str()); + ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "OnPreRehash called with param %s", parameter.c_str()); // Send out to other servers if (!parameter.empty() && parameter[0] != '-') { - parameterlist params; - params.push_back(parameter); - Utils->DoOneToAllButSender(user ? user->uuid : ServerInstance->Config->GetSID(), "REHASH", params, user ? user->server : ServerInstance->Config->ServerName); + CmdBuilder params(user ? user : ServerInstance->FakeClient, "REHASH"); + params.push(parameter); + params.Forward(user ? TreeServer::Get(user)->GetRoute() : NULL); } } -void ModuleSpanningTree::OnRehash(User* user) +void ModuleSpanningTree::ReadConfig(ConfigStatus& status) { + // Did this rehash change the description of this server? + const std::string& newdesc = ServerInstance->Config->ServerDesc; + if (newdesc != Utils->TreeRoot->GetDesc()) + { + // Broadcast a SINFO desc message to let the network know about the new description. This is the description + // string that is sent in the SERVER message initially and shown for example in WHOIS. + // We don't need to update the field itself in the Server object - the core does that. + CommandSInfo::Builder(Utils->TreeRoot, "desc", newdesc).Broadcast(); + } + // Re-read config stuff try { @@ -731,8 +656,8 @@ void ModuleSpanningTree::OnRehash(User* user) std::string msg = "Error in configuration: "; msg.append(e.GetReason()); ServerInstance->SNO->WriteToSnoMask('l', msg); - if (user && !IS_LOCAL(user)) - ServerInstance->PI->SendSNONotice("L", msg); + if (status.srcuser && !IS_LOCAL(status.srcuser)) + ServerInstance->PI->SendSNONotice('L', msg); } } @@ -747,177 +672,149 @@ void ModuleSpanningTree::OnLoadModule(Module* mod) data.push_back('='); data.append(v.link_data); } - ServerInstance->PI->SendMetaData(NULL, "modules", data); + ServerInstance->PI->SendMetaData("modules", data); } void ModuleSpanningTree::OnUnloadModule(Module* mod) { - ServerInstance->PI->SendMetaData(NULL, "modules", "-" + mod->ModuleSourceFile); + if (!Utils) + return; + ServerInstance->PI->SendMetaData("modules", "-" + mod->ModuleSourceFile); - unsigned int items = Utils->TreeRoot->ChildCount(); - for(unsigned int x = 0; x < items; x++) + if (mod == this) + { + // We are being unloaded, inform modules about all servers splitting which cannot be done later when the servers are actually disconnected + const server_hash& servers = Utils->serverlist; + for (server_hash::const_iterator i = servers.begin(); i != servers.end(); ++i) + { + TreeServer* server = i->second; + if (!server->IsRoot()) + FOREACH_MOD_CUSTOM(GetLinkEventProvider(), ServerProtocol::LinkEventListener, OnServerSplit, (server, false)); + } + return; + } + + // Some other module is being unloaded. If it provides an IOHook we use, we must close that server connection now. + +restart: + // Close all connections which use an IO hook provided by this module + const TreeServer::ChildServers& list = Utils->TreeRoot->GetChildren(); + for (TreeServer::ChildServers::const_iterator i = list.begin(); i != list.end(); ++i) { - TreeServer* srv = Utils->TreeRoot->GetChild(x); - TreeSocket* sock = srv->GetSocket(); - if (sock && sock->GetIOHook() == mod) + TreeSocket* sock = (*i)->GetSocket(); + if (sock->GetModHook(mod)) { sock->SendError("SSL module unloaded"); sock->Close(); + // XXX: The list we're iterating is modified by TreeServer::SQuit() which is called by Close() + goto restart; } } + + for (SpanningTreeUtilities::TimeoutList::const_iterator i = Utils->timeoutlist.begin(); i != Utils->timeoutlist.end(); ++i) + { + TreeSocket* sock = i->first; + if (sock->GetModHook(mod)) + sock->Close(); + } } -// note: the protocol does not allow direct umode +o except -// via NICK with 8 params. sending OPERTYPE infers +o modechange -// locally. void ModuleSpanningTree::OnOper(User* user, const std::string &opertype) { if (user->registered != REG_ALL || !IS_LOCAL(user)) return; - parameterlist params; - params.push_back(opertype); - Utils->DoOneToMany(user->uuid,"OPERTYPE",params); + + // Note: The protocol does not allow direct umode +o; + // sending OPERTYPE infers +o modechange locally. + CommandOpertype::Builder(user).Broadcast(); } void ModuleSpanningTree::OnAddLine(User* user, XLine *x) { - if (!x->IsBurstable() || loopCall) + if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user))) return; - parameterlist params; - params.push_back(x->type); - params.push_back(x->Displayable()); - params.push_back(ServerInstance->Config->ServerName); - params.push_back(ConvToStr(x->set_time)); - params.push_back(ConvToStr(x->duration)); - params.push_back(":" + x->reason); - if (!user) - { - /* Server-set lines */ - Utils->DoOneToMany(ServerInstance->Config->GetSID(), "ADDLINE", params); - } - else if (IS_LOCAL(user)) - { - /* User-set lines */ - Utils->DoOneToMany(user->uuid, "ADDLINE", params); - } + user = ServerInstance->FakeClient; + + CommandAddLine::Builder(x, user).Broadcast(); } void ModuleSpanningTree::OnDelLine(User* user, XLine *x) { - if (!x->IsBurstable() || loopCall) + if (!x->IsBurstable() || loopCall || (user && !IS_LOCAL(user))) return; - parameterlist params; - params.push_back(x->type); - params.push_back(x->Displayable()); - if (!user) - { - /* Server-unset lines */ - Utils->DoOneToMany(ServerInstance->Config->GetSID(), "DELLINE", params); - } - else if (IS_LOCAL(user)) - { - /* User-unset lines */ - Utils->DoOneToMany(user->uuid, "DELLINE", params); - } + user = ServerInstance->FakeClient; + + CmdBuilder params(user, "DELLINE"); + params.push(x->type); + params.push(x->Displayable()); + params.Broadcast(); } -void ModuleSpanningTree::OnMode(User* user, void* dest, int target_type, const parameterlist &text, const std::vector &translate) +void ModuleSpanningTree::OnUserAway(User* user) { - if ((IS_LOCAL(user)) && (user->registered == REG_ALL)) - { - parameterlist params; - std::string output_text; - - ServerInstance->Parser->TranslateUIDs(translate, text, output_text); - - if (target_type == TYPE_USER) - { - User* u = (User*)dest; - params.push_back(u->uuid); - params.push_back(output_text); - Utils->DoOneToMany(user->uuid, "MODE", params); - } - else - { - Channel* c = (Channel*)dest; - params.push_back(c->name); - params.push_back(ConvToStr(c->age)); - params.push_back(output_text); - Utils->DoOneToMany(user->uuid, "FMODE", params); - } - } + if (IS_LOCAL(user)) + CommandAway::Builder(user).Broadcast(); } -ModResult ModuleSpanningTree::OnSetAway(User* user, const std::string &awaymsg) +void ModuleSpanningTree::OnUserBack(User* user) { if (IS_LOCAL(user)) - { - parameterlist params; - if (!awaymsg.empty()) - { - params.push_back(ConvToStr(user->awaytime)); - params.push_back(":" + awaymsg); - } - Utils->DoOneToMany(user->uuid, "AWAY", params); - } - - return MOD_RES_PASSTHRU; + CommandAway::Builder(user).Broadcast(); } -void ModuleSpanningTree::ProtoSendMode(void* opaque, TargetTypeFlags target_type, void* target, const parameterlist &modeline, const std::vector &translate) +void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags) { - TreeSocket* s = (TreeSocket*)opaque; - std::string output_text; + if (processflags & ModeParser::MODE_LOCALONLY) + return; - ServerInstance->Parser->TranslateUIDs(translate, modeline, output_text); + if (u) + { + if (u->registered != REG_ALL) + return; - if (target) + CmdBuilder params(source, "MODE"); + params.push(u->uuid); + params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes)); + params.push_raw(Translate::ModeChangeListToParams(modes.getlist())); + params.Broadcast(); + } + else { - if (target_type == TYPE_USER) - { - User* u = (User*)target; - s->WriteLine(":"+ServerInstance->Config->GetSID()+" MODE "+u->uuid+" "+output_text); - } - else if (target_type == TYPE_CHANNEL) - { - Channel* c = (Channel*)target; - s->WriteLine(":"+ServerInstance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+output_text); - } + CmdBuilder params(source, "FMODE"); + params.push(c->name); + params.push_int(c->age); + params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes)); + params.push_raw(Translate::ModeChangeListToParams(modes.getlist())); + params.Broadcast(); } } -void ModuleSpanningTree::ProtoSendMetaData(void* opaque, Extensible* target, const std::string &extname, const std::string &extdata) +void ModuleSpanningTree::OnShutdown(const std::string& reason) { - TreeSocket* s = static_cast(opaque); - User* u = dynamic_cast(target); - Channel* c = dynamic_cast(target); - if (u) - s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+u->uuid+" "+extname+" :"+extdata); - else if (c) - s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA "+c->name+" "+ConvToStr(c->age)+" "+extname+" :"+extdata); - else if (!target) - s->WriteLine(":"+ServerInstance->Config->GetSID()+" METADATA * "+extname+" :"+extdata); + const TreeServer::ChildServers& children = Utils->TreeRoot->GetChildren(); + while (!children.empty()) + children.front()->SQuit(reason, true); } CullResult ModuleSpanningTree::cull() { - Utils->cull(); + if (Utils) + Utils->cull(); return this->Module::cull(); } ModuleSpanningTree::~ModuleSpanningTree() { - delete ServerInstance->PI; - ServerInstance->PI = new ProtocolInterface; + ServerInstance->PI = &ServerInstance->DefaultProtocolInterface; - /* This will also free the listeners */ - delete Utils; + Server* newsrv = new Server(ServerInstance->Config->GetSID(), ServerInstance->Config->ServerName, ServerInstance->Config->ServerDesc); + SetLocalUsersServer(newsrv); - delete commands; + delete Utils; } Version ModuleSpanningTree::GetVersion() @@ -929,12 +826,13 @@ Version ModuleSpanningTree::GetVersion() * so that any activity it sees is FINAL, e.g. we arent going to send out * a NICK message before m_cloaking has finished putting the +x on the user, * etc etc. - * Therefore, we return PRIORITY_LAST to make sure we end up at the END of + * Therefore, we set our priority to PRIORITY_LAST to make sure we end up at the END of * the module call queue. */ void ModuleSpanningTree::Prioritize() { ServerInstance->Modules->SetPriority(this, PRIORITY_LAST); + ServerInstance->Modules.SetPriority(this, I_OnPreTopicChange, PRIORITY_FIRST); } MODULE_INIT(ModuleSpanningTree)