]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_spanningtree.cpp
Added <oper:swhois> to m_swhois, which will override <type:swhois> if specified
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
index cb806a28c3ec32a272db9fe10ae99929062b78c7..ae68115c5074a61c6c9c0b97663707fb696f3bc7 100644 (file)
 
 /** 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
@@ -125,6 +131,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<TreeSocket*> Bindings;
@@ -1058,6 +1067,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<std::string> params;
                        params.push_back(Current->GetName());
                        params.push_back(":"+reason);
@@ -1589,7 +1601,7 @@ 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)
@@ -1611,7 +1623,7 @@ class TreeSocket : public InspSocket
                        /* 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);
@@ -1915,6 +1927,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");
@@ -1949,42 +1980,42 @@ class TreeSocket : public InspSocket
                /* Yes, these arent too nice looking, but they get the job done */
                for (std::vector<ZLine*>::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+                       snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
                        buffer.append(data);
                }
                for (std::vector<QLine*>::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+                       snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
                        buffer.append(data);
                }
                for (std::vector<GLine*>::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s\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<ELine*>::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s\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<ZLine*>::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+                       snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
                        buffer.append(data);
                }
                for (std::vector<QLine*>::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
+                       snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
                        buffer.append(data);
                }
                for (std::vector<GLine*>::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s\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<ELine*>::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++, iterations++)
                {
-                       snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s\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 +2090,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 +2659,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<std::string> par;
                                        par.push_back(prefix);
                                        par.push_back(signon);
@@ -2688,6 +2719,45 @@ class TreeSocket : public InspSocket
                return true;
        }
 
+       bool HandleSetTime(const std::string &prefix, std::deque<std::string> &params)
+       {
+               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<std::string> oparams;
+                       oparams.push_back(ConvToStr(us));
+                       
+                       Utils->DoOneToMany(prefix, "TIMESET", oparams);
+               }
+               
+               return true;
+       }
+
        bool Time(const std::string &prefix, std::deque<std::string> &params)
        {
                // :source.server TIME remote.server sendernick
@@ -2700,9 +2770,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]);
                                }
@@ -3073,20 +3141,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->Log(DEBUG, "Timestamps are equal; pat yourself on the back");
+                                               }
+                                               else if (force || (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, "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 +3383,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 +3427,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 +3473,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 +3490,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 +3665,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 +3710,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)
        {
        }
 
@@ -3800,33 +3951,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)
+                               irc::portparser portrange(Port, false);
+                               int portno = -1;
+                               while ((portno = portrange.GetToken()))
                                {
-                                       ServerInstance->Log(DEFAULT,"m_spanningtree: Binding server port %s:%d successful!", IP.c_str(), Port);
-                                       Bindings.push_back(listener);
-                               }
-                               else
-                               {
-                                       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 +4016,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 +4060,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 +4084,7 @@ class ModuleSpanningTree : public Module
        SpanningTreeUtilities* Utils;
 
  public:
+       TimeSyncTimer *SyncTimer;
 
        ModuleSpanningTree(InspIRCd* Me)
                : Module::Module(Me), max_local(0), max_global(0)
@@ -3924,6 +4093,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 +4157,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 +4511,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,6 +4614,13 @@ class ModuleSpanningTree : public Module
                return 1;
        }
 
+       void BroadcastTimeSync()
+       {
+               std::deque<std::string> 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')
@@ -4683,6 +4886,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 +5043,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<std::string> 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<std::string> params;
+                       params.push_back(data);
+                       Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
+               }
+               else
+               {
+                       if (IS_LOCAL(source))
                        {
-                               std::deque<std::string> 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<std::string> params;
+                                       params.push_back(host);
+                                       params.push_back(sduration);
+                                       params.push_back(":"+reason);
+                                       Utils->DoOneToMany(source->nick,stype,params);
+                               }
+                               else
+                               {
+                                       std::deque<std::string> params;
+                                       params.push_back(host);
+                                       Utils->DoOneToMany(source->nick,stype,params);
+                               }
                        }
                }
        }
@@ -4996,7 +5220,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 +5245,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 +5292,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 +5325,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 +5370,6 @@ Link* SpanningTreeUtilities::FindLink(const std::string& name)
        return NULL;
 }
 
-
 class ModuleSpanningTreeFactory : public ModuleFactory
 {
  public: