X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree.cpp;h=60eb650dd17748aa078a723db55f2b78a62a1e66;hb=5da606a02777de3d91f3b88d278ce49cadb67af5;hp=58eb520f034fc15921bb5a997bfe03270754496d;hpb=73d36e20ed9f7ccdb6ce4e745a5eeed065f73bef;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree.cpp b/src/modules/m_spanningtree.cpp index 58eb520f0..60eb650dd 100644 --- a/src/modules/m_spanningtree.cpp +++ b/src/modules/m_spanningtree.cpp @@ -30,8 +30,14 @@ /** If you make a change which breaks the protocol, increment this. * If you completely change the protocol, completely change the number. + * + * IMPORTANT: If you make changes, document your changes here, without fail: + * http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions + * + * Failure to document your protocol changes will result in a painfully + * painful death by pain. You have been warned. */ -const long ProtocolVersion = 1101; +const long ProtocolVersion = 1102; /* * The server list in InspIRCd is maintained as two structures @@ -80,6 +86,7 @@ class ModuleSpanningTree; */ typedef nspace::hash_map, irc::StrHashComp> server_hash; +typedef std::map TreeServerList; /** The Link class might as well be a struct, * but this is C++ and we don't believe in structs (!). @@ -125,6 +132,9 @@ class SpanningTreeUtilities /** Announce TS changes to channels on merge */ bool AnnounceTSChange; + /** Synchronize timestamps between servers + */ + bool EnableTimeSync; /** Socket bindings for listening sockets */ std::vector Bindings; @@ -170,10 +180,10 @@ class SpanningTreeUtilities void ReadConfiguration(bool rebind); /** Add a server to the server list for GetListOfServersForChannel */ - void AddThisServer(TreeServer* server, std::deque &list); + void AddThisServer(TreeServer* server, TreeServerList &list); /** Compile a list of servers which contain members of channel c */ - void GetListOfServersForChannel(chanrec* c, std::deque &list); + void GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list); /** Find a server by name */ TreeServer* FindServer(const std::string &ServerName); @@ -1058,6 +1068,9 @@ class TreeSocket : public InspSocket { if ((Current) && (Current != Utils->TreeRoot)) { + Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server"); + rmode.Send(Instance); + std::deque params; params.push_back(Current->GetName()); params.push_back(":"+reason); @@ -1579,6 +1592,7 @@ class TreeSocket : public InspSocket userrec* who = NULL; /* User we are currently checking */ std::string channel = params[0]; /* Channel name, as a string */ time_t TS = atoi(params[1].c_str()); /* Timestamp given to us for remote side */ + bool created = false; /* Try and find the channel */ chanrec* chan = this->Instance->FindChan(channel); @@ -1589,11 +1603,13 @@ class TreeSocket : public InspSocket /* default TS is a high value, which if we dont have this * channel will let the other side apply their modes. */ - time_t ourTS = time(NULL)+600; + time_t ourTS = Instance->Time(true)+600; /* Does this channel exist? if it does, get its REAL timestamp */ if (chan) ourTS = chan->age; + else + created = true; /* don't perform deops, and set TS to correct time after processing. */ /* In 1.1, if they have the newer channel, we immediately clear * all status modes from our users. We then accept their modes. @@ -1605,18 +1621,19 @@ class TreeSocket : public InspSocket { std::deque param_list; - if (chan) - chan->age = TS; - /* Lower the TS here */ if (Utils->AnnounceTSChange && chan) chan->WriteChannelWithServ(Instance->Config->ServerName, - "TS for %s changed from %lu to %lu", chan->name, ourTS, TS); + "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS); ourTS = TS; - param_list.push_back(channel); - /* Zap all the privilage modes on our side */ - this->RemoveStatus(Instance->Config->ServerName, param_list); + + /* Zap all the privilage modes on our side, if the channel exists here */ + if (!created) + { + this->RemoveStatus(Instance->Config->ServerName, param_list); + chan->age = TS; + } } /* Put the final parameter of the FJOIN into a tokenstream ready to split it */ @@ -1789,6 +1806,20 @@ class TreeSocket : public InspSocket free(mode_users[f]); } + /* if we newly created the channel, set it's TS properly. */ + if (created) + { + /* find created channel .. */ + chan = this->Instance->FindChan(channel); + if (chan) + /* w00t said this shouldnt be needed but it is. + * This isnt strictly true, as chan can be NULL + * if a nick collision has occured and therefore + * the channel was never created. + */ + chan->age = TS; + } + /* All done. That wasnt so bad was it, you can wipe * the sweat from your forehead now. :-) */ @@ -1915,6 +1946,25 @@ class TreeSocket : public InspSocket if (numusers) buffer.append(list).append("\r\n"); + /* Sorry for the hax. Because newly created channels assume +nt, + * if this channel doesnt have +nt, explicitly send -n and -t for the missing modes. + */ + bool inverted = false; + if (!c->IsModeSet('n')) + { + modes.append("-n"); + inverted = true; + } + if (!c->IsModeSet('t')) + { + modes.append("-t"); + inverted = true; + } + if (inverted) + { + modes.append("+"); + } + for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++) { modes.append("b"); @@ -1959,12 +2009,12 @@ class TreeSocket : public InspSocket } for (std::vector::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s\r\n",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s\r\n",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++, iterations++) @@ -1979,12 +2029,12 @@ class TreeSocket : public InspSocket } for (std::vector::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s\r\n",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } for (std::vector::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++, iterations++) { - snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s\r\n",sn,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); + snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason); buffer.append(data); } @@ -2059,7 +2109,7 @@ class TreeSocket : public InspSocket */ void DoBurst(TreeServer* s) { - std::string burst = "BURST "+ConvToStr(time(NULL)); + std::string burst = "BURST "+ConvToStr(Instance->Time(true)); std::string endburst = "ENDBURST"; // Because by the end of the netburst, it could be gone! std::string name = s->GetName(); @@ -2628,7 +2678,7 @@ class TreeSocket : public InspSocket char idle[MAXBUF]; snprintf(signon,MAXBUF,"%lu",(unsigned long)x->signon); - snprintf(idle,MAXBUF,"%lu",(unsigned long)abs((x->idle_lastmsg)-time(NULL))); + snprintf(idle,MAXBUF,"%lu",(unsigned long)abs((x->idle_lastmsg)-Instance->Time(true))); std::deque par; par.push_back(prefix); par.push_back(signon); @@ -2688,6 +2738,45 @@ class TreeSocket : public InspSocket return true; } + bool HandleSetTime(const std::string &prefix, std::deque ¶ms) + { + if (!params.size() || !Utils->EnableTimeSync) + return true; + + bool force = false; + + if ((params.size() == 2) && (params[1] == "FORCE")) + force = true; + + time_t rts = atoi(params[0].c_str()); + time_t us = Instance->Time(true); + + if (rts == us) + { + Instance->Log(DEBUG, "Timestamp from %s is equal", prefix.c_str()); + + Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix); + } + else if (force || (rts < us)) + { + int old = Instance->SetTimeDelta(rts - us); + Instance->Log(DEBUG, "%s TS (diff %d) from %s applied (old delta was %d)", (force) ? "Forced" : "Lower", rts - us, prefix.c_str(), old); + + Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix); + } + else + { + Instance->Log(DEBUG, "Higher TS (diff %d) from %s overridden", us - rts, prefix.c_str()); + + std::deque oparams; + oparams.push_back(ConvToStr(us)); + + Utils->DoOneToMany(prefix, "TIMESET", oparams); + } + + return true; + } + bool Time(const std::string &prefix, std::deque ¶ms) { // :source.server TIME remote.server sendernick @@ -2700,9 +2789,7 @@ class TreeSocket : public InspSocket userrec* u = this->Instance->FindNick(params[1]); if (u) { - char curtime[256]; - snprintf(curtime,256,"%lu",(unsigned long)time(NULL)); - params.push_back(curtime); + params.push_back(ConvToStr(Instance->Time(false))); params[0] = prefix; Utils->DoOneToOne(this->Instance->Config->ServerName,"TIME",params,params[0]); } @@ -2977,11 +3064,11 @@ class TreeSocket : public InspSocket irc::string command; std::string prefix; + line = line.substr(0, line.find_first_of("\r\n")); + if (line.empty()) return true; - line = line.substr(0, line.find_first_of("\r\n")); - Instance->Log(DEBUG,"IN: %s", line.c_str()); this->Split(line.c_str(),params); @@ -3073,20 +3160,40 @@ class TreeSocket : public InspSocket } else if (command == "BURST") { - if (params.size()) + if (params.size() && Utils->EnableTimeSync) { - /* If a time stamp is provided, try and check syncronization */ - time_t THEM = atoi(params[0].c_str()); - long delta = THEM-time(NULL); + /* If a time stamp is provided, apply synchronization */ + bool force = false; + time_t them = atoi(params[0].c_str()); + time_t us = Instance->Time(true); + int delta = them - us; + + if ((params.size() == 2) && (params[1] == "FORCE")) + force = true; + if ((delta < -600) || (delta > 600)) { this->Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than ten minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta)); this->WriteLine("ERROR :Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than ten minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!"); return false; } - else if ((delta < -60) || (delta > 60)) + + if (us == them) { - this->Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds, please consider synching your clocks.",abs(delta)); + this->Instance->Log(DEBUG, "Timestamps are equal; pat yourself on the back"); + } + else if (force || (us > them)) + { + this->Instance->Log(DEBUG, "Remote server has lower TS (%d seconds)", them - us); + this->Instance->SetTimeDelta(them - us); + // Send this new timestamp to any other servers + Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params); + } + else + { + // Override the timestamp + this->Instance->Log(DEBUG, "We have a higher timestamp (by %d seconds), not updating delta", us - them); + this->WriteLine(":" + Utils->TreeRoot->GetName() + " TIMESET " + ConvToStr(us)); } } this->LinkState = CONNECTED; @@ -3295,6 +3402,10 @@ class TreeSocket : public InspSocket { return this->Push(prefix,params); } + else if (command == "TIMESET") + { + return this->HandleSetTime(prefix, params); + } else if (command == "TIME") { return this->Time(prefix,params); @@ -3335,6 +3446,42 @@ class TreeSocket : public InspSocket } return true; } + else if (command == "OPERNOTICE") + { + std::string sourceserv = this->myhost; + + if (this->InboundServerName != "") + sourceserv = this->InboundServerName; + + if (params.size() >= 1) + Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]); + + return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); + } + else if (command == "MODENOTICE") + { + std::string sourceserv = this->myhost; + if (this->InboundServerName != "") + sourceserv = this->InboundServerName; + if (params.size() >= 2) + { + Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str()); + } + + return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); + } + else if (command == "SNONOTICE") + { + std::string sourceserv = this->myhost; + if (this->InboundServerName != "") + sourceserv = this->InboundServerName; + if (params.size() >= 2) + { + Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]); + } + + return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params); + } else if (command == "ENDBURST") { this->bursting = false; @@ -3345,6 +3492,10 @@ class TreeSocket : public InspSocket sourceserv = this->InboundServerName; } this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str()); + + Event rmode((char*)sourceserv.c_str(), (Module*)Utils->Creator, "new_server"); + rmode.Send(Instance); + return true; } else @@ -3358,6 +3509,25 @@ class TreeSocket : public InspSocket { sourceserv = this->InboundServerName; } + if ((!who) && (command == "MODE")) + { + if (Utils->IsServer(prefix)) + { + const char* modelist[127]; + for (size_t i = 0; i < params.size(); i++) + modelist[i] = params[i].c_str(); + + userrec* fake = new userrec(Instance); + fake->SetFd(FD_MAGIC_NUMBER); + + this->Instance->SendMode(modelist, params.size(), fake); + + delete fake; + + /* Hot potato! pass it on! */ + return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params); + } + } if (who) { if ((command == "NICK") && (params.size() > 0)) @@ -3514,7 +3684,7 @@ class ServernameResolver : public Resolver Link MyLink; SpanningTreeUtilities* Utils; public: - ServernameResolver(SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD), MyLink(x), Utils(Util) + ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, me), MyLink(x), Utils(Util) { /* Nothing in here, folks */ } @@ -3559,7 +3729,7 @@ class SecurityIPResolver : public Resolver Link MyLink; SpanningTreeUtilities* Utils; public: - SecurityIPResolver(SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD), MyLink(x), Utils(U) + SecurityIPResolver(Module* me, SpanningTreeUtilities* U, InspIRCd* Instance, const std::string &hostname, Link x) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, me), MyLink(x), Utils(U) { } @@ -3607,25 +3777,34 @@ SpanningTreeUtilities::~SpanningTreeUtilities() delete TreeRoot; } -void SpanningTreeUtilities::AddThisServer(TreeServer* server, std::deque &list) +void SpanningTreeUtilities::AddThisServer(TreeServer* server, TreeServerList &list) { - for (unsigned int c = 0; c < list.size(); c++) - { - if (list[c] == server) - { - return; - } - } - list.push_back(server); + if (list.find(server) == list.end()) + list[server] = server; } /** returns a list of DIRECT servernames for a specific channel */ -void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, std::deque &list) +void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list) { - CUList *ulist = c->GetUsers(); + CUList *ulist; + switch (status) + { + case '@': + ulist = c->GetOppedUsers(); + break; + case '%': + ulist = c->GetHalfoppedUsers(); + break; + case '+': + ulist = c->GetVoicedUsers(); + break; + default: + ulist = c->GetUsers(); + break; + } for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++) { - if (i->second->GetFd() < 0) + if ((i->second->GetFd() < 0) && (exempt_list.find(i->second) == exempt_list.end())) { TreeServer* best = this->BestRouteTo(i->second->server); if (best) @@ -3637,6 +3816,7 @@ void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, std::deque ¶ms) { + char pfx = 0; TreeServer* omitroute = this->BestRouteTo(omit); if ((command == "NOTICE") || (command == "PRIVMSG")) { @@ -3645,6 +3825,7 @@ bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, cons /* Prefixes */ if ((*(params[0].c_str()) == '@') || (*(params[0].c_str()) == '%') || (*(params[0].c_str()) == '+')) { + pfx = params[0][0]; params[0] = params[0].substr(1, params[0].length()-1); } if ((*(params[0].c_str()) != '#') && (*(params[0].c_str()) != '$')) @@ -3671,15 +3852,18 @@ bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, cons else { chanrec* c = ServerInstance->FindChan(params[0]); - if (c) + userrec* u = ServerInstance->FindNick(prefix); + if (c && u) { - std::deque list; - GetListOfServersForChannel(c,list); - unsigned int lsize = list.size(); - for (unsigned int i = 0; i < lsize; i++) + CUList elist; + TreeServerList list; + FOREACH_MOD(I_OnBuildExemptList, OnBuildExemptList((command == "PRIVMSG" ? MSG_PRIVMSG : MSG_NOTICE), c, u, pfx, elist)); + GetListOfServersForChannel(c,list,pfx,elist); + + for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) { - TreeSocket* Sock = list[i]->GetSocket(); - if ((Sock) && (list[i]->GetName() != omit) && (omitroute != list[i])) + TreeSocket* Sock = i->second->GetSocket(); + if ((Sock) && (i->second->GetName() != omit) && (omitroute != i->second)) { Sock->WriteLine(data); } @@ -3800,33 +3984,38 @@ void SpanningTreeUtilities::ReadConfiguration(bool rebind) { std::string Type = Conf->ReadValue("bind","type",j); std::string IP = Conf->ReadValue("bind","address",j); - int Port = Conf->ReadInteger("bind","port",j,true); + std::string Port = Conf->ReadValue("bind","port",j); if (Type == "servers") { - ServerInstance->Log(DEBUG,"m_spanningtree: Binding server port %s:%d", IP.c_str(), Port); - if (IP == "*") - { - IP = ""; - } - TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(),Port,true,10); - if (listener->GetState() == I_LISTENING) - { - ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), Port); - Bindings.push_back(listener); - } - else + irc::portparser portrange(Port, false); + int portno = -1; + while ((portno = portrange.GetToken())) { - ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %d",Port); - listener->Close(); - DELETE(listener); + ServerInstance->Log(DEBUG,"m_spanningtree: Binding server port %s:%d", IP.c_str(), portno); + if (IP == "*") + IP = ""; + + TreeSocket* listener = new TreeSocket(this, ServerInstance, IP.c_str(), portno, true, 10); + if (listener->GetState() == I_LISTENING) + { + ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), portno); + Bindings.push_back(listener); + } + else + { + ServerInstance->Log(DEFAULT,"m_spanningtree: Warning: Failed to bind server port %s:%d",IP.c_str(), portno); + listener->Close(); + DELETE(listener); + } + ServerInstance->Log(DEBUG,"Done with this binding"); } - ServerInstance->Log(DEBUG,"Done with this binding"); } } } FlatLinks = Conf->ReadFlag("options","flatlinks",0); HideULines = Conf->ReadFlag("options","hideulines",0); AnnounceTSChange = Conf->ReadFlag("options","announcets",0); + EnableTimeSync = !(Conf->ReadFlag("options","notimesync",0)); LinkBlocks.clear(); ValidIPs.clear(); for (int j =0; j < Conf->Enumerate("link"); j++) @@ -3860,7 +4049,7 @@ void SpanningTreeUtilities::ReadConfiguration(bool rebind) { try { - SecurityIPResolver* sr = new SecurityIPResolver(this, ServerInstance, L.IPAddr, L); + SecurityIPResolver* sr = new SecurityIPResolver((Module*)this->Creator, this, ServerInstance, L.IPAddr, L); ServerInstance->AddResolver(sr); } catch (ModuleException& e) @@ -3904,7 +4093,19 @@ void SpanningTreeUtilities::ReadConfiguration(bool rebind) DELETE(Conf); } - +/** To create a timer which recurs every second, we inherit from InspTimer. + * InspTimer is only one-shot however, so at the end of each Tick() we simply + * insert another of ourselves into the pending queue :) + */ +class TimeSyncTimer : public InspTimer +{ + private: + InspIRCd *Instance; + ModuleSpanningTree *Module; + public: + TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod); + virtual void Tick(time_t TIME); +}; class ModuleSpanningTree : public Module { @@ -3916,6 +4117,7 @@ class ModuleSpanningTree : public Module SpanningTreeUtilities* Utils; public: + TimeSyncTimer *SyncTimer; ModuleSpanningTree(InspIRCd* Me) : Module::Module(Me), max_local(0), max_global(0) @@ -3924,6 +4126,14 @@ class ModuleSpanningTree : public Module command_rconnect = new cmd_rconnect(ServerInstance, this, Utils); ServerInstance->AddCommand(command_rconnect); + + if (Utils->EnableTimeSync) + { + SyncTimer = new TimeSyncTimer(ServerInstance, this); + ServerInstance->Timers->AddTimer(SyncTimer); + } + else + SyncTimer = NULL; } void ShowLinks(TreeServer* Current, userrec* user, int hops) @@ -3980,14 +4190,33 @@ class ModuleSpanningTree : public Module if (n_users > max_global) max_global = n_users; - user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),this->CountServs()); + unsigned int ulined_count = 0; + unsigned int ulined_local_count = 0; + + /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden, + * locally and globally (locally means directly connected to us) + */ + if ((Utils->HideULines) && (!*user->oper)) + { + for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++) + { + if (ServerInstance->ULine(q->second->GetName().c_str())) + { + ulined_count++; + if (q->second->GetParent() == Utils->TreeRoot) + ulined_local_count++; + } + } + } + + user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs()); if (ServerInstance->OperCount()) user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount()); if (ServerInstance->UnregisteredUserCount()) user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount()); if (ServerInstance->ChannelCount()) user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount()); - user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),this->CountLocalServs()); + user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs()); user->WriteServ("265 %s :Current Local Users: %d Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local); user->WriteServ("266 %s :Current Global Users: %d Max: %d",user->nick,n_users,max_global); return; @@ -4315,7 +4544,7 @@ class ModuleSpanningTree : public Module { try { - ServernameResolver* snr = new ServernameResolver(Utils, ServerInstance,x->IPAddr, *x); + ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x); ServerInstance->AddResolver(snr); } catch (ModuleException& e) @@ -4418,14 +4647,22 @@ class ModuleSpanningTree : public Module return 1; } + void BroadcastTimeSync() + { + std::deque params; + params.push_back(ConvToStr(ServerInstance->Time(true))); + Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params); + } + virtual int OnStats(char statschar, userrec* user, string_list &results) { - if (statschar == 'c') + if ((statschar == 'c') || (statschar == 'n')) { for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++) { - results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" C *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].EncryptionKey != "" ? 'e' : '-')+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s'); - results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str()); + results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].EncryptionKey != "" ? 'e' : '-')+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s'); + if (statschar == 'c') + results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str()); } results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report"); ServerInstance->SNO->WriteToSnoMask('t',"Notice: %s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host); @@ -4561,7 +4798,7 @@ class ModuleSpanningTree : public Module } } - virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status) + virtual void OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { if (target_type == TYPE_USER) { @@ -4585,12 +4822,12 @@ class ModuleSpanningTree : public Module std::string cname = c->name; if (status) cname = status + cname; - std::deque list; - Utils->GetListOfServersForChannel(c,list); - unsigned int ucount = list.size(); - for (unsigned int i = 0; i < ucount; i++) + TreeServerList list; + Utils->GetListOfServersForChannel(c,list,status,exempt_list); + + for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) { - TreeSocket* Sock = list[i]->GetSocket(); + TreeSocket* Sock = i->second->GetSocket(); if (Sock) Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text); } @@ -4610,7 +4847,7 @@ class ModuleSpanningTree : public Module } } - virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status) + virtual void OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list) { if (target_type == TYPE_USER) { @@ -4636,12 +4873,12 @@ class ModuleSpanningTree : public Module std::string cname = c->name; if (status) cname = status + cname; - std::deque list; - Utils->GetListOfServersForChannel(c,list); - unsigned int ucount = list.size(); - for (unsigned int i = 0; i < ucount; i++) + TreeServerList list; + Utils->GetListOfServersForChannel(c,list,status,exempt_list); + + for (TreeServerList::iterator i = list.begin(); i != list.end(); i++) { - TreeSocket* Sock = list[i]->GetSocket(); + TreeSocket* Sock = i->second->GetSocket(); if (Sock) Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text); } @@ -4683,6 +4920,14 @@ class ModuleSpanningTree : public Module params.push_back(ConvToStr(channel->age)); params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick)); Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params); + if (channel->GetUserCounter() == 1) + { + /* First user in, sync the modes for the channel */ + params.pop_back(); + /* This is safe, all inspircd servers default to +nt */ + params.push_back("+nt"); + Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params); + } } } @@ -4832,26 +5077,39 @@ class ModuleSpanningTree : public Module void OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason) { - if (IS_LOCAL(source)) + if (!source) { - char type[8]; - snprintf(type,8,"%cLINE",linetype); - std::string stype = type; - if (adding) - { - char sduration[MAXBUF]; - snprintf(sduration,MAXBUF,"%ld",duration); - std::deque params; - params.push_back(host); - params.push_back(sduration); - params.push_back(":"+reason); - Utils->DoOneToMany(source->nick,stype,params); - } - else + /* Server-set lines */ + char data[MAXBUF]; + snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false), + (unsigned long)duration, reason.c_str()); + std::deque params; + params.push_back(data); + Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params); + } + else + { + if (IS_LOCAL(source)) { - std::deque params; - params.push_back(host); - Utils->DoOneToMany(source->nick,stype,params); + char type[8]; + snprintf(type,8,"%cLINE",linetype); + std::string stype = type; + if (adding) + { + char sduration[MAXBUF]; + snprintf(sduration,MAXBUF,"%ld",duration); + std::deque params; + params.push_back(host); + params.push_back(sduration); + params.push_back(":"+reason); + Utils->DoOneToMany(source->nick,stype,params); + } + else + { + std::deque params; + params.push_back(host); + Utils->DoOneToMany(source->nick,stype,params); + } } } } @@ -4996,7 +5254,7 @@ class ModuleSpanningTree : public Module return; (*params)[1] = ":" + (*params)[1]; params->insert(params->begin() + 1,ServerInstance->Config->ServerName); - params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time())); + params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true))); Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params); } else if (event->GetEventID() == "send_mode") @@ -5021,6 +5279,46 @@ class ModuleSpanningTree : public Module params->insert(params->begin() + 1,ConvToStr(ourTS)); Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params); } + else if (event->GetEventID() == "send_mode_explicit") + { + if (params->size() < 2) + return; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params); + } + else if (event->GetEventID() == "send_opers") + { + if (params->size() < 1) + return; + (*params)[0] = ":" + (*params)[0]; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params); + } + else if (event->GetEventID() == "send_modeset") + { + if (params->size() < 2) + return; + (*params)[1] = ":" + (*params)[1]; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params); + } + else if (event->GetEventID() == "send_snoset") + { + if (params->size() < 2) + return; + (*params)[1] = ":" + (*params)[1]; + Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params); + } + else if (event->GetEventID() == "send_push") + { + if (params->size() < 2) + return; + + userrec *a = ServerInstance->FindNick((*params)[0]); + + if (!a) + return; + + (*params)[1] = ":" + (*params)[1]; + Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server); + } } virtual ~ModuleSpanningTree() @@ -5028,6 +5326,8 @@ class ModuleSpanningTree : public Module ServerInstance->Log(DEBUG,"Performing unload of spanningtree!"); /* This will also free the listeners */ delete Utils; + if (SyncTimer) + ServerInstance->Timers->DelTimer(SyncTimer); } virtual Version GetVersion() @@ -5059,6 +5359,17 @@ class ModuleSpanningTree : public Module } }; +TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(43200, Inst->Time()), Instance(Inst), Module(Mod) +{ +} + +void TimeSyncTimer::Tick(time_t TIME) +{ + Module->BroadcastTimeSync(); + Module->SyncTimer = new TimeSyncTimer(Instance, Module); + Instance->Timers->AddTimer(Module->SyncTimer); +} + void SpanningTreeUtilities::DoFailOver(Link* x) { if (x->FailOver.length()) @@ -5093,7 +5404,6 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name) return NULL; } - class ModuleSpanningTreeFactory : public ModuleFactory { public: