]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_spanningtree.cpp
Fixed a crash when spanningtree is given a blank line; really simple fix, and I had...
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
index 93996bcdbd11c26a776e61c894c5a557ebfa864b..60eb650dd17748aa078a723db55f2b78a62a1e66 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
@@ -80,6 +86,7 @@ class ModuleSpanningTree;
  */
 typedef nspace::hash_map<std::string, TreeServer*, nspace::hash<string>, irc::StrHashComp> server_hash;
 
+typedef std::map<TreeServer*,TreeServer*> TreeServerList;
 
 /** The Link class might as well be a struct,
  * but this is C++ and we don't believe in structs (!).
@@ -173,10 +180,10 @@ class SpanningTreeUtilities
        void ReadConfiguration(bool rebind);
        /** Add a server to the server list for GetListOfServersForChannel
         */
-       void AddThisServer(TreeServer* server, std::deque<TreeServer*> &list);
+       void AddThisServer(TreeServer* server, TreeServerList &list);
        /** Compile a list of servers which contain members of channel c
         */
-       void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list);
+       void GetListOfServersForChannel(chanrec* c, TreeServerList &list, char status, const CUList &exempt_list);
        /** Find a server by name
         */
        TreeServer* FindServer(const std::string &ServerName);
@@ -1585,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);
@@ -1600,6 +1608,8 @@ class TreeSocket : public InspSocket
                /* 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.
@@ -1611,18 +1621,19 @@ class TreeSocket : public InspSocket
                {
                        std::deque<std::string> param_list;
 
-                       if (chan)
-                               chan->age = TS;
-
                        /* Lower the TS here */
                        if (Utils->AnnounceTSChange && chan)
                                chan->WriteChannelWithServ(Instance->Config->ServerName,
                                "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 */
@@ -1795,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. :-)
                 */
@@ -1921,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");
@@ -3020,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);
@@ -3402,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;
@@ -3697,25 +3777,34 @@ SpanningTreeUtilities::~SpanningTreeUtilities()
        delete TreeRoot;
 }
 
-void SpanningTreeUtilities::AddThisServer(TreeServer* server, std::deque<TreeServer*> &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<TreeServer*> &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)
@@ -3727,6 +3816,7 @@ void SpanningTreeUtilities::GetListOfServersForChannel(chanrec* c, std::deque<Tr
 
 bool SpanningTreeUtilities::DoOneToAllButSenderRaw(const std::string &data, const std::string &omit, const std::string &prefix, const irc::string &command, std::deque<std::string> &params)
 {
+       char pfx = 0;
        TreeServer* omitroute = this->BestRouteTo(omit);
        if ((command == "NOTICE") || (command == "PRIVMSG"))
        {
@@ -3735,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()) != '$'))
@@ -3761,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<TreeServer*> 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);
                                                }
@@ -4096,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;
@@ -4543,12 +4656,13 @@ class ModuleSpanningTree : public Module
 
        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 ? "<hidden>" : 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 ? "<hidden>" : 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);
@@ -4684,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)
                {
@@ -4708,12 +4822,12 @@ class ModuleSpanningTree : public Module
                                        std::string cname = c->name;
                                        if (status)
                                                cname = status + cname;
-                                       std::deque<TreeServer*> 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);
                                        }
@@ -4733,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)
                {
@@ -4759,12 +4873,12 @@ class ModuleSpanningTree : public Module
                                        std::string cname = c->name;
                                        if (status)
                                                cname = status + cname;
-                                       std::deque<TreeServer*> 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);
                                        }
@@ -4806,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);
+                       }
                }
        }
 
@@ -5161,9 +5283,29 @@ class ModuleSpanningTree : public Module
                {
                        if (params->size() < 2)
                                return;
-                       // Insert the TS value of the object, either userrec or chanrec
                        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)