]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/modules/m_spanningtree.cpp
I hate typos
[user/henk/code/inspircd.git] / src / modules / m_spanningtree.cpp
index 8da6fe43e3a8b49c31a810e76f94b343840677c2..e3aea5f0480d13fe5d3b255f620ed156b8fb69d5 100644 (file)
@@ -14,6 +14,8 @@
  * ---------------------------------------------------
  */
 
+/* $ModDesc: Povides a spanning tree server link protocol */
+
 using namespace std;
 
 #include <stdio.h>
@@ -35,6 +37,7 @@ using namespace std;
 #include "inspstring.h"
 #include "hashcomp.h"
 #include "message.h"
+#include "xline.h"
 
 #ifdef GCC3
 #define nspace __gnu_cxx
@@ -60,15 +63,27 @@ extern chan_hash chanlist;
 class TreeServer;
 class TreeSocket;
 
+TreeServer *TreeRoot;
+
+typedef nspace::hash_map<std::string, TreeServer*> server_hash;
+server_hash serverlist;
+
 bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string> params, std::string target);
 bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std::string> params, std::string omit);
 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> params);
 bool DoOneToAllButSenderRaw(std::string data,std::string omit, std::string prefix,std::string command,std::deque<std::string> params);
 void ReadConfiguration(bool rebind);
 
+extern std::vector<KLine> klines;
+extern std::vector<GLine> glines;
+extern std::vector<ZLine> zlines;
+extern std::vector<QLine> qlines;
+extern std::vector<ELine> elines;
+
 class TreeServer
 {
        TreeServer* Parent;
+       TreeServer* Route;
        std::vector<TreeServer*> Children;
        std::string ServerName;
        std::string ServerDesc;
@@ -97,6 +112,8 @@ class TreeServer
                VersionString = "";
                UserCount = OperCount = 0;
                VersionString = GetVersionString();
+               Route = NULL;
+               AddHashEntry();
        }
 
        TreeServer(std::string Name, std::string Desc, TreeServer* Above, TreeSocket* Sock) : Parent(Above), ServerName(Name), ServerDesc(Desc), Socket(Sock)
@@ -105,6 +122,80 @@ class TreeServer
                UserCount = OperCount = 0;
                this->SetNextPingTime(time(NULL) + 60);
                this->SetPingFlag();
+
+               /* find the 'route' for this server (e.g. the one directly connected
+                * to the local server, which we can use to reach it)
+                *
+                * In the following example, consider we have just added a TreeServer
+                * class for server G on our network, of which we are server A.
+                * To route traffic to G (marked with a *) we must send the data to
+                * B (marked with a +) so this algorithm initializes the 'Route'
+                * value to point at whichever server traffic must be routed through
+                * to get here. If we were to try this algorithm with server B,
+                * the Route pointer would point at its own object ('this').
+                *
+                *              A
+                *             / \
+                *          + B   C
+                *           / \   \
+                *          D   E   F
+                *         /         \
+                *      * G           H
+                *
+                * We only run this algorithm when a server is created, as
+                * the routes remain constant while ever the server exists, and
+                * do not need to be re-calculated.
+                */
+
+               Route = Above;
+               if (Route == TreeRoot)
+               {
+                       Route = this;
+               }
+               else
+               {
+                       while (this->Route->GetParent() != TreeRoot)
+                       {
+                               this->Route = Route->GetParent();
+                       }
+               }
+
+               /* Because recursive code is slow and takes a lot of resources,
+                * we store two representations of the server tree. The first
+                * is a recursive structure where each server references its
+                * children and its parent, which is used for netbursts and
+                * netsplits to dump the whole dataset to the other server,
+                * and the second is used for very fast lookups when routing
+                * messages and is instead a hash_map, where each item can
+                * be referenced by its server name. The AddHashEntry()
+                * call below automatically inserts each TreeServer class
+                * into the hash_map as it is created. There is a similar
+                * maintainance call in the destructor to tidy up deleted
+                * servers.
+                */
+
+               this->AddHashEntry();
+       }
+
+       void AddHashEntry()
+       {
+               server_hash::iterator iter;
+               iter = serverlist.find(this->ServerName);
+               if (iter == serverlist.end())
+                       serverlist[this->ServerName] = this;
+       }
+
+       void DelHashEntry()
+       {
+               server_hash::iterator iter;
+               iter = serverlist.find(this->ServerName);
+               if (iter != serverlist.end())
+                       serverlist.erase(iter);
+       }
+
+       TreeServer* GetRoute()
+       {
+               return Route;
        }
 
        std::string GetName()
@@ -203,7 +294,10 @@ class TreeServer
                return false;
        }
 
-       // removes child nodes of this node, and of that node, etc etc
+       /* Removes child nodes of this node, and of that node, etc etc.
+        * This is used during netsplits to automatically tidy up the
+        * server tree. It is slow, we don't use it for much else.
+        */
        bool Tidy()
        {
                bool stillchildren = true;
@@ -222,6 +316,11 @@ class TreeServer
                }
                return true;
        }
+
+       ~TreeServer()
+       {
+               this->DelHashEntry();
+       }
 };
 
 class Link
@@ -236,89 +335,49 @@ class Link
         time_t NextConnectTime;
 };
 
-/* $ModDesc: Povides a spanning tree server link protocol */
-
 Server *Srv;
 ConfigReader *Conf;
-TreeServer *TreeRoot;
 std::vector<Link> LinkBlocks;
 
-TreeServer* RouteEnumerate(TreeServer* Current, std::string ServerName)
+TreeServer* FindServer(std::string ServerName)
 {
-       if (Current->GetName() == ServerName)
-               return Current;
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
+       server_hash::iterator iter;
+       iter = serverlist.find(ServerName);
+       if (iter != serverlist.end())
        {
-               TreeServer* found = RouteEnumerate(Current->GetChild(q),ServerName);
-               if (found)
-               {
-                       return found;
-               }
+               return iter->second;
+       }
+       else
+       {
+               return NULL;
        }
-       return NULL;
 }
 
-// Returns the locally connected server we must route a
-// message through to reach server 'ServerName'. This
-// only applies to one-to-one and not one-to-many routing.
+/* Returns the locally connected server we must route a
+ * message through to reach server 'ServerName'. This
+ * only applies to one-to-one and not one-to-many routing.
+ * See the comments for the constructor of TreeServer
+ * for more details.
+ */
 TreeServer* BestRouteTo(std::string ServerName)
 {
        if (ServerName.c_str() == TreeRoot->GetName())
-       {
                return NULL;
-       }
-       // first, find the server by recursively walking the tree
-       TreeServer* Found = RouteEnumerate(TreeRoot,ServerName);
-       // did we find it? If not, they did something wrong, abort.
-       if (!Found)
+       TreeServer* Found = FindServer(ServerName);
+       if (Found)
        {
-               return NULL;
+               return Found->GetRoute();
        }
        else
        {
-               // The server exists, follow its parent nodes until
-               // the parent of the current is 'TreeRoot', we know
-               // then that this is a directly-connected server.
-               while ((Found) && (Found->GetParent() != TreeRoot))
-               {
-                       Found = Found->GetParent();
-               }
-               return Found;
-       }
-}
-
-bool LookForServer(TreeServer* Current, std::string ServerName)
-{
-       if (ServerName == Current->GetName())
-               return true;
-       for (unsigned int q = 0; q < Current->ChildCount(); q++)
-       {
-               if (LookForServer(Current->GetChild(q),ServerName))
-                       return true;
+               return NULL;
        }
-       return false;
 }
 
 TreeServer* Found;
 
-void RFindServer(TreeServer* Current, std::string ServerName)
-{
-       if ((ServerName == Current->GetName()) && (!Found))
-       {
-               Found = Current;
-               return;
-       }
-       if (!Found)
-       {
-               for (unsigned int q = 0; q < Current->ChildCount(); q++)
-               {
-                       if (!Found)
-                               RFindServer(Current->GetChild(q),ServerName);
-               }
-       }
-       return;
-}
-
+/* TODO: These need optimizing to use an iterator of serverlist
+ */
 void RFindServerMask(TreeServer* Current, std::string ServerName)
 {
        if (Srv->MatchText(Current->GetName(),ServerName) && (!Found))
@@ -336,13 +395,6 @@ void RFindServerMask(TreeServer* Current, std::string ServerName)
        }
 }
 
-TreeServer* FindServer(std::string ServerName)
-{
-       Found = NULL;
-       RFindServer(TreeRoot,ServerName);
-       return Found;
-}
-
 TreeServer* FindServerMask(std::string ServerName)
 {
        Found = NULL;
@@ -352,7 +404,7 @@ TreeServer* FindServerMask(std::string ServerName)
 
 bool IsServer(std::string ServerName)
 {
-       return LookForServer(TreeRoot,ServerName);
+       return (FindServer(ServerName) != NULL);
 }
 
 class TreeSocket : public InspSocket
@@ -527,10 +579,15 @@ class TreeSocket : public InspSocket
                {
                        if ((ts >= c->topicset) || (!*c->topic))
                        {
+                               std::string oldtopic = c->topic;
                                strlcpy(c->topic,topic.c_str(),MAXTOPIC);
                                strlcpy(c->setby,setby.c_str(),NICKMAX);
                                c->topicset = ts;
-                               WriteChannelWithServ((char*)source.c_str(), c, "TOPIC %s :%s", c->name, c->topic);
+                               // if the topic text is the same as the current topic,
+                               // dont bother to send the TOPIC command out, just silently
+                               // update the set time and set nick.
+                               if (oldtopic != topic)
+                                       WriteChannelWithServ((char*)source.c_str(), c, "TOPIC %s :%s", c->name, c->topic);
                        }
                        
                }
@@ -569,7 +626,7 @@ class TreeSocket : public InspSocket
 
                // default is a high value, which if we dont have this
                // channel will let the other side apply their modes.
-               time_t ourTS = time(NULL)+20;
+               time_t ourTS = time(NULL)+600;
                chanrec* us = Srv->FindChannel(channel);
                if (us)
                {
@@ -740,6 +797,32 @@ class TreeSocket : public InspSocket
                }
        }
 
+       void SendXLines(TreeServer* Current)
+       {
+               char data[MAXBUF];
+               // for zlines and qlines, we should first check if theyre global...
+               for (std::vector<ZLine>::iterator i = zlines.begin(); i != zlines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->ipaddr,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+               for (std::vector<QLine>::iterator i = qlines.begin(); i != qlines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->nick,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+               for (std::vector<GLine>::iterator i = glines.begin(); i != glines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE G %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+               for (std::vector<ELine>::iterator i = elines.begin(); i != elines.end(); i++)
+               {
+                       snprintf(data,MAXBUF,":%s ADDLINE E %s %s %lu %lu :%s",Srv->GetServerName().c_str(),i->hostmask,i->source,(unsigned long)i->set_time,(unsigned long)i->duration,i->reason);
+                       this->WriteLine(data);
+               }
+       }
+
        void SendChannelModes(TreeServer* Current)
        {
                char data[MAXBUF];
@@ -798,6 +881,7 @@ class TreeSocket : public InspSocket
                this->SendUsers(s);
                // Send everything else (channel modes etc)
                this->SendChannelModes(s);
+               this->SendXLines(s);
                this->WriteLine("ENDBURST");
        }
 
@@ -861,7 +945,7 @@ class TreeSocket : public InspSocket
                        {
                                strcat(u->modes,"o");
                        }
-                       DoOneToAllButSender(u->server,"OPERTYPE",params,u->server);
+                       DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
                }
                return true;
        }
@@ -928,6 +1012,72 @@ class TreeSocket : public InspSocket
                DoOneToAllButSender(prefix,"VERSION",params,prefix);
                return true;
        }
+
+       bool ChangeHost(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 1)
+                       return true;
+               userrec* u = Srv->FindNick(prefix);
+               if (u)
+               {
+                       Srv->ChangeHost(u,params[0]);
+                       DoOneToAllButSender(prefix,"FHOST",params,u->server);
+               }
+               return true;
+       }
+
+       bool AddLine(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 6)
+                       return true;
+               std::string linetype = params[0]; /* Z, Q, E, G, K */
+               std::string mask = params[1]; /* Line type dependent */
+               std::string source = params[2]; /* may not be online or may be a server */
+               std::string settime = params[3]; /* EPOCH time set */
+               std::string duration = params[4]; /* Duration secs */
+               std::string reason = params[5];
+
+               switch (*(linetype.c_str()))
+               {
+                       case 'Z':
+                               add_zline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'Q':
+                               add_qline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'E':
+                               add_eline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'G':
+                               add_gline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       case 'K':
+                               add_kline(atoi(duration.c_str()), source.c_str(), reason.c_str(), mask.c_str());
+                       break;
+                       default:
+                               /* Just in case... */
+                               Srv->SendOpers("*** \2WARNING\2: Invalid xline type '"+linetype+"' sent by server "+prefix+", ignored!");
+                       break;
+               }
+               /* Send it on its way */
+               params[5] = ":" + params[5];
+               DoOneToAllButSender(prefix,"ADDLINE",params,prefix);
+               return true;
+       }
+
+       bool ChangeName(std::string prefix, std::deque<std::string> params)
+       {
+               if (params.size() < 1)
+                       return true;
+               userrec* u = Srv->FindNick(prefix);
+               if (u)
+               {
+                       Srv->ChangeGECOS(u,params[0]);
+                       params[0] = ":" + params[0];
+                       DoOneToAllButSender(prefix,"FNAME",params,u->server);
+               }
+               return true;
+       }
        
        bool LocalPing(std::string prefix, std::deque<std::string> params)
        {
@@ -1245,6 +1395,18 @@ class TreeSocket : public InspSocket
                                {
                                        return this->ServerVersion(prefix,params);
                                }
+                               else if (command == "FHOST")
+                               {
+                                       return this->ChangeHost(prefix,params);
+                               }
+                               else if (command == "FNAME")
+                               {
+                                       return this->ChangeName(prefix,params);
+                               }
+                               else if (command == "ADDLINE")
+                               {
+                                       return this->AddLine(prefix,params);
+                               }
                                else if (command == "SQUIT")
                                {
                                        if (params.size() == 2)
@@ -1353,11 +1515,11 @@ void AddThisServer(TreeServer* server, std::deque<TreeServer*> &list)
 }
 
 // returns a list of DIRECT servernames for a specific channel
-std::deque<TreeServer*> GetListOfServersForChannel(chanrec* c)
+void GetListOfServersForChannel(chanrec* c, std::deque<TreeServer*> &list)
 {
-       std::deque<TreeServer*> list;
        std::vector<char*> *ulist = c->GetUsers();
-       for (unsigned int i = 0; i < ulist->size(); i++)
+       unsigned int ucount = ulist->size();
+       for (unsigned int i = 0; i < ucount; i++)
        {
                char* o = (*ulist)[i];
                userrec* otheruser = (userrec*)o;
@@ -1368,7 +1530,7 @@ std::deque<TreeServer*> GetListOfServersForChannel(chanrec* c)
                                AddThisServer(best,list);
                }
        }
-       return list;
+       return;
 }
 
 bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix,std::string command,std::deque<std::string> params)
@@ -1385,7 +1547,6 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
                                if (d)
                                {
                                        std::deque<std::string> par;
-                                       par.clear();
                                        par.push_back(params[0]);
                                        par.push_back(":"+params[1]);
                                        DoOneToOne(prefix,command,par,d->server);
@@ -1398,9 +1559,11 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
                                chanrec* c = Srv->FindChannel(params[0]);
                                if (c)
                                {
-                                       std::deque<TreeServer*> list = GetListOfServersForChannel(c);
+                                       std::deque<TreeServer*> list;
+                                       GetListOfServersForChannel(c,list);
                                        log(DEBUG,"Got a list of %d servers",list.size());
-                                       for (unsigned int i = 0; i < list.size(); i++)
+                                       unsigned int lsize = list.size();
+                                       for (unsigned int i = 0; i < lsize; i++)
                                        {
                                                TreeSocket* Sock = list[i]->GetSocket();
                                                if ((Sock) && (list[i]->GetName() != omit) && (omitroute != list[i]))
@@ -1414,7 +1577,8 @@ bool DoOneToAllButSenderRaw(std::string data,std::string omit,std::string prefix
                        }
                }
        }
-       for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
+       unsigned int items = TreeRoot->ChildCount();
+       for (unsigned int x = 0; x < items; x++)
        {
                TreeServer* Route = TreeRoot->GetChild(x);
                if ((Route->GetSocket()) && (Route->GetName() != omit) && (omitroute != Route))
@@ -1430,11 +1594,13 @@ bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std
 {
        TreeServer* omitroute = BestRouteTo(omit);
        std::string FullLine = ":" + prefix + " " + command;
-       for (unsigned int x = 0; x < params.size(); x++)
+       unsigned int words = params.size();
+       for (unsigned int x = 0; x < words; x++)
        {
                FullLine = FullLine + " " + params[x];
        }
-       for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
+       unsigned int items = TreeRoot->ChildCount();
+       for (unsigned int x = 0; x < items; x++)
        {
                TreeServer* Route = TreeRoot->GetChild(x);
                // Send the line IF:
@@ -1453,11 +1619,13 @@ bool DoOneToAllButSender(std::string prefix, std::string command, std::deque<std
 bool DoOneToMany(std::string prefix, std::string command, std::deque<std::string> params)
 {
        std::string FullLine = ":" + prefix + " " + command;
-       for (unsigned int x = 0; x < params.size(); x++)
+       unsigned int words = params.size();
+       for (unsigned int x = 0; x < words; x++)
        {
                FullLine = FullLine + " " + params[x];
        }
-       for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++)
+       unsigned int items = TreeRoot->ChildCount();
+       for (unsigned int x = 0; x < items; x++)
        {
                TreeServer* Route = TreeRoot->GetChild(x);
                if (Route->GetSocket())
@@ -1475,7 +1643,8 @@ bool DoOneToOne(std::string prefix, std::string command, std::deque<std::string>
        if (Route)
        {
                std::string FullLine = ":" + prefix + " " + command;
-               for (unsigned int x = 0; x < params.size(); x++)
+               unsigned int words = params.size();
+               for (unsigned int x = 0; x < words; x++)
                {
                        FullLine = FullLine + " " + params[x];
                }
@@ -1918,8 +2087,10 @@ class ModuleSpanningTree : public Module
                        if (std::string(user->server) == Srv->GetServerName())
                        {
                                chanrec *c = (chanrec*)dest;
-                               std::deque<TreeServer*> list = GetListOfServersForChannel(c);
-                               for (unsigned int i = 0; i < list.size(); i++)
+                               std::deque<TreeServer*> list;
+                               GetListOfServersForChannel(c,list);
+                               unsigned int ucount = list.size();
+                               for (unsigned int i = 0; i < ucount; i++)
                                {
                                        TreeSocket* Sock = list[i]->GetSocket();
                                        if (Sock)
@@ -1950,8 +2121,10 @@ class ModuleSpanningTree : public Module
                        if (std::string(user->server) == Srv->GetServerName())
                        {
                                chanrec *c = (chanrec*)dest;
-                               std::deque<TreeServer*> list = GetListOfServersForChannel(c);
-                               for (unsigned int i = 0; i < list.size(); i++)
+                               std::deque<TreeServer*> list;
+                               GetListOfServersForChannel(c,list);
+                               unsigned int ucount = list.size();
+                               for (unsigned int i = 0; i < ucount; i++)
                                {
                                        TreeSocket* Sock = list[i]->GetSocket();
                                        if (Sock)
@@ -2000,12 +2173,27 @@ class ModuleSpanningTree : public Module
                }
        }
 
+       virtual void OnChangeHost(userrec* user, std::string newhost)
+       {
+               // only occurs for local clients
+               std::deque<std::string> params;
+               params.push_back(newhost);
+               DoOneToMany(user->nick,"FHOST",params);
+       }
+
+       virtual void OnChangeName(userrec* user, std::string gecos)
+       {
+               // only occurs for local clients
+               std::deque<std::string> params;
+               params.push_back(gecos);
+               DoOneToMany(user->nick,"FNAME",params);
+       }
+
        virtual void OnUserPart(userrec* user, chanrec* channel)
        {
                if (std::string(user->server) == Srv->GetServerName())
                {
                        std::deque<std::string> params;
-                       params.clear();
                        params.push_back(channel->name);
                        DoOneToMany(user->nick,"PART",params);
                }
@@ -2018,7 +2206,6 @@ class ModuleSpanningTree : public Module
                {
                        std::deque<std::string> params;
                        snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
-                       params.clear();
                        params.push_back(agestr);
                        params.push_back(user->nick);
                        params.push_back(user->host);
@@ -2101,6 +2288,72 @@ class ModuleSpanningTree : public Module
                }
        }
 
+       void OnLine(userrec* source, std::string host, bool adding, char linetype, long duration, std::string reason)
+       {
+               if (std::string(source->server) == Srv->GetServerName())
+               {
+                       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);
+                               DoOneToMany(source->nick,stype,params);
+                       }
+                       else
+                       {
+                               std::deque<std::string> params;
+                               params.push_back(host);
+                               DoOneToMany(source->nick,stype,params);
+                       }
+               }
+       }
+
+       virtual void OnAddGLine(long duration, userrec* source, std::string reason, std::string hostmask)
+       {
+               OnLine(source,hostmask,true,'G',duration,reason);
+       }
+       
+       virtual void OnAddZLine(long duration, userrec* source, std::string reason, std::string ipmask)
+       {
+               OnLine(source,ipmask,true,'Z',duration,reason);
+       }
+
+       virtual void OnAddQLine(long duration, userrec* source, std::string reason, std::string nickmask)
+       {
+               OnLine(source,nickmask,true,'Q',duration,reason);
+       }
+
+       virtual void OnAddELine(long duration, userrec* source, std::string reason, std::string hostmask)
+       {
+               OnLine(source,hostmask,true,'E',duration,reason);
+       }
+
+       virtual void OnDelGLine(userrec* source, std::string hostmask)
+       {
+               OnLine(source,hostmask,false,'G',0,"");
+       }
+
+       virtual void OnDelZLine(userrec* source, std::string ipmask)
+       {
+               OnLine(source,ipmask,false,'Z',0,"");
+       }
+
+       virtual void OnDelQLine(userrec* source, std::string nickmask)
+       {
+               OnLine(source,nickmask,false,'Q',0,"");
+       }
+
+       virtual void OnDelELine(userrec* source, std::string hostmask)
+       {
+               OnLine(source,hostmask,false,'E',0,"");
+       }
+
        virtual void OnMode(userrec* user, void* dest, int target_type, std::string text)
        {
                if (std::string(user->server) == Srv->GetServerName())