X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Fm_spanningtree.cpp;h=fa22f80a1555bd523e0328078845a33f21e23990;hb=8b126657e79754e1d843da6c48d770937b589dab;hp=0029c614781b717f4993211b474422d2077bf2ec;hpb=e30ca6c388269537295bccabd22aa5d716b9dc46;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/modules/m_spanningtree.cpp b/src/modules/m_spanningtree.cpp index 0029c6147..fa22f80a1 100644 --- a/src/modules/m_spanningtree.cpp +++ b/src/modules/m_spanningtree.cpp @@ -45,11 +45,19 @@ using namespace std; enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED }; typedef nspace::hash_map, irc::StrHashComp> user_hash; +typedef nspace::hash_map, irc::StrHashComp> chan_hash; + extern user_hash clientlist; +extern chan_hash chanlist; class TreeServer; class TreeSocket; +bool DoOneToOne(std::string prefix, std::string command, std::deque params, std::string target); +bool DoOneToAllButSender(std::string prefix, std::string command, std::deque params, std::string omit); +bool DoOneToMany(std::string prefix, std::string command, std::deque params); +bool DoOneToAllButSenderRaw(std::string data,std::string omit); + class TreeServer { TreeServer* Parent; @@ -154,6 +162,26 @@ class TreeServer } return false; } + + // removes child nodes of this node, and of that node, etc etc + bool Tidy() + { + bool stillchildren = true; + while (stillchildren) + { + stillchildren = false; + for (std::vector::iterator a = Children.begin(); a < Children.end(); a++) + { + TreeServer* s = (TreeServer*)*a; + s->Tidy(); + Children.erase(a); + delete s; + stillchildren = true; + break; + } + } + return true; + } }; class Link @@ -194,6 +222,11 @@ TreeServer* RouteEnumerate(TreeServer* Current, std::string ServerName) TreeServer* BestRouteTo(std::string ServerName) { log(DEBUG,"Finding best route to %s",ServerName.c_str()); + if (ServerName.c_str() == TreeRoot->GetName()) + { + log(DEBUG,"Cant route to myself!!!"); + 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. @@ -216,6 +249,51 @@ TreeServer* BestRouteTo(std::string ServerName) } } +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 false; +} + +TreeServer* Found; + +void RFindServer(TreeServer* Current, std::string ServerName) +{ + if ((ServerName == Current->GetName()) && (!Found)) + { + Found = Current; + log(DEBUG,"Found server %s desc %s",Current->GetName().c_str(),Current->GetDesc().c_str()); + return; + } + if (!Found) + { + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + if (!Found) + RFindServer(Current->GetChild(q),ServerName); + } + } + return; +} + +TreeServer* FindServer(std::string ServerName) +{ + Found = NULL; + RFindServer(TreeRoot,ServerName); + return Found; +} + +bool IsServer(std::string ServerName) +{ + return LookForServer(TreeRoot,ServerName); +} + class TreeSocket : public InspSocket { std::string myhost; @@ -223,6 +301,8 @@ class TreeSocket : public InspSocket ServerState LinkState; std::string InboundServerName; std::string InboundDescription; + int num_lost_users; + int num_lost_servers; public: @@ -298,8 +378,165 @@ class TreeSocket : public InspSocket } } + void SquitServer(TreeServer* Current) + { + // recursively squit the servers attached to 'Current' + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + TreeServer* recursive_server = Current->GetChild(q); + this->SquitServer(recursive_server); + } + // Now we've whacked the kids, whack self + log(DEBUG,"Deleted %s",Current->GetName().c_str()); + num_lost_servers++; + bool quittingpeople = true; + while (quittingpeople) + { + quittingpeople = false; + for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++) + { + if (!strcasecmp(u->second->server,Current->GetName().c_str())) + { + log(DEBUG,"Quitting user %s of server %s",u->second->nick,u->second->server); + Srv->QuitUser(u->second,Current->GetName()+" "+std::string(Srv->GetServerName())); + num_lost_users++; + quittingpeople = true; + break; + } + } + } + } + + void Squit(TreeServer* Current,std::string reason) + { + if (Current) + { + std::deque params; + params.push_back(Current->GetName()); + params.push_back(":"+reason); + DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName()); + if (Current->GetParent() == TreeRoot) + { + Srv->SendOpers("Server \002"+Current->GetName()+"\002 split: "+reason); + } + else + { + Srv->SendOpers("Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason); + } + num_lost_servers = 0; + num_lost_users = 0; + SquitServer(Current); + Current->Tidy(); + Current->GetParent()->DelChild(Current); + delete Current; + WriteOpers("Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers); + } + else + { + log(DEBUG,"Squit from unknown server"); + } + } + + bool ForceMode(std::string source, std::deque params) + { + userrec* who = new userrec; + who->fd = FD_MAGIC_NUMBER; + if (params.size() < 2) + return true; + char* modelist[255]; + for (unsigned int q = 0; q < params.size(); q++) + { + modelist[q] = (char*)params[q].c_str(); + } + Srv->SendMode(modelist,params.size(),who); + DoOneToAllButSender(source,"FMODE",params,source); + delete who; + return true; + } + + bool ForceTopic(std::string source, std::deque params) + { + // FTOPIC %s %lu %s :%s + if (params.size() != 4) + return true; + std::string channel = params[0]; + time_t ts = atoi(params[1].c_str()); + std::string setby = params[2]; + std::string topic = params[3]; + + chanrec* c = Srv->FindChannel(channel); + if (c) + { + if ((ts >= c->topicset) || (!*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); + } + + } + + // all done, send it on its way + params[3] = ":" + params[3]; + DoOneToAllButSender(source,"FTOPIC",params,source); + + return true; + } + + bool ForceJoin(std::string source, std::deque params) + { + if (params.size() < 1) + return true; + for (unsigned int channelnum = 0; channelnum < params.size(); channelnum++) + { + // process one channel at a time, applying modes. + char* channel = (char*)params[channelnum].c_str(); + char permissions = *channel; + char* mode = NULL; + switch (permissions) + { + case '@': + channel++; + mode = "+o"; + break; + case '%': + channel++; + mode = "+h"; + break; + case '+': + channel++; + mode = "+v"; + break; + } + userrec* who = Srv->FindNick(source); + if (who) + { + char* key = ""; + chanrec* chan = Srv->FindChannel(channel); + if ((chan) && (*chan->key)) + { + key = chan->key; + } + Srv->JoinUserToChannel(who,channel,key); + if (mode) + { + char* modelist[3]; + modelist[0] = channel; + modelist[1] = mode; + modelist[2] = who->nick; + Srv->SendMode(modelist,3,who); + } + DoOneToAllButSender(source,"FJOIN",params,who->server); + } + } + return true; + } + bool IntroduceClient(std::string source, std::deque params) { + if (params.size() < 8) + return true; // NICK age nick host dhost ident +modes ip :gecos // 0 1 2 3 4 5 6 7 std::string nick = params[1]; @@ -308,6 +545,12 @@ class TreeSocket : public InspSocket std::string ident = params[4]; time_t age = atoi(params[0].c_str()); std::string modes = params[5]; + if (*(modes.c_str()) == '+') + { + char* m = (char*)modes.c_str(); + m++; + modes = m; + } std::string ip = params[6]; std::string gecos = params[7]; char* tempnick = (char*)nick.c_str(); @@ -318,10 +561,11 @@ class TreeSocket : public InspSocket if (iter != clientlist.end()) { // nick collision - log(DEBUG,"Nick collision on %s!%s@%s",tempnick,ident.c_str(),host.c_str()); + log(DEBUG,"Nick collision on %s!%s@%s: %lu %lu",tempnick,ident.c_str(),host.c_str(),(unsigned long)age,(unsigned long)iter->second->age); + this->WriteLine(":"+Srv->GetServerName()+" KILL "+tempnick+" :Nickname collision"); return true; } - + clientlist[tempnick] = new userrec(); clientlist[tempnick]->fd = FD_MAGIC_NUMBER; strlcpy(clientlist[tempnick]->nick, tempnick,NICKMAX); @@ -338,9 +582,31 @@ class TreeSocket : public InspSocket clientlist[tempnick]->chans[i].channel = NULL; clientlist[tempnick]->chans[i].uc_modes = 0; } + params[7] = ":" + params[7]; + DoOneToAllButSender(source,"NICK",params,source); return true; } + void SendChannelModes(TreeServer* Current) + { + char data[MAXBUF]; + for (chan_hash::iterator c = chanlist.begin(); c != chanlist.end(); c++) + { + snprintf(data,MAXBUF,":%s FMODE %s +%s",Srv->GetServerName().c_str(),c->second->name,chanmodes(c->second)); + this->WriteLine(data); + if (*c->second->topic) + { + snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",Srv->GetServerName().c_str(),c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic); + this->WriteLine(data); + } + for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++) + { + snprintf(data,MAXBUF,":%s FMODE %s +b %s",Srv->GetServerName().c_str(),c->second->name,b->data); + this->WriteLine(data); + } + } + } + // send all users and their channels void SendUsers(TreeServer* Current) { @@ -371,6 +637,7 @@ class TreeSocket : public InspSocket // Send users and their channels this->SendUsers(s); // TODO: Send everything else (channel modes etc) + this->SendChannelModes(s); this->WriteLine("ENDBURST"); } @@ -421,6 +688,86 @@ class TreeSocket : public InspSocket return false; } + bool OperType(std::string prefix, std::deque params) + { + if (params.size() != 1) + return true; + std::string opertype = params[0]; + userrec* u = Srv->FindNick(prefix); + if (u) + { + strlcpy(u->oper,opertype.c_str(),NICKMAX); + if (!strchr(u->modes,'o')) + { + strcat(u->modes,"o"); + } + DoOneToAllButSender(u->server,"OPERTYPE",params,u->server); + } + return true; + } + + bool RemoteRehash(std::string prefix, std::deque params) + { + if (params.size() < 1) + return true; + std::string servermask = params[0]; + if (Srv->MatchText(Srv->GetServerName(),servermask)) + { + Srv->RehashServer(); + } + DoOneToAllButSender(prefix,"REHASH",params,prefix); + return; + } + + bool RemoteKill(std::string prefix, std::deque params) + { + if (params.size() != 2) + return true; + std::string nick = params[0]; + std::string reason = params[1]; + userrec* u = Srv->FindNick(prefix); + userrec* who = Srv->FindNick(nick); + if (who) + { + std::string sourceserv = prefix; + if (u) + { + sourceserv = u->server; + } + params[1] = ":" + params[1]; + DoOneToAllButSender(prefix,"KILL",params,sourceserv); + Srv->QuitUser(who,reason); + } + return true; + } + + bool RemoteServer(std::string prefix, std::deque params) + { + if (params.size() < 4) + return false; + std::string servername = params[0]; + std::string password = params[1]; + int hops = atoi(params[2].c_str()); + std::string description = params[3]; + if (!hops) + { + this->WriteLine("ERROR :Protocol error - Introduced remote server with incorrect hopcount!"); + return false; + } + TreeServer* ParentOfThis = FindServer(prefix); + if (!ParentOfThis) + { + this->WriteLine("ERROR :Protocol error - Introduced remote server from unknown server "+prefix); + return false; + } + TreeServer* Node = new TreeServer(servername,description,ParentOfThis,NULL); + ParentOfThis->AddChild(Node); + params[3] = ":" + params[3]; + DoOneToAllButSender(prefix,"SERVER",params,prefix); + Srv->SendOpers("*** Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")"); + return true; + } + bool Outbound_Reply_Server(std::deque params) { if (params.size() < 4) @@ -448,6 +795,8 @@ class TreeSocket : public InspSocket // node. TreeServer* Node = new TreeServer(servername,description,TreeRoot,this); TreeRoot->AddChild(Node); + params[3] = ":" + params[3]; + DoOneToAllButSender(TreeRoot->GetName(),"SERVER",params,servername); this->DoBurst(Node); return true; } @@ -473,7 +822,7 @@ class TreeSocket : public InspSocket { if ((x->Name == servername) && (x->RecvPass == password)) { - Srv->SendOpers("*** Verified incoming server connection from "+servername+"["+this->GetIP()+"] ("+description+")"); + Srv->SendOpers("*** Verified incoming server connection from \002"+servername+"\002["+this->GetIP()+"] ("+description+")"); this->InboundServerName = servername; this->InboundDescription = description; // this is good. Send our details: Our server name and description and hopcount of 0, @@ -488,7 +837,7 @@ class TreeSocket : public InspSocket return false; } - std::deque Split(std::string line) + std::deque Split(std::string line, bool stripcolon) { std::deque n; std::stringstream s(line); @@ -498,33 +847,41 @@ class TreeSocket : public InspSocket while (!s.eof()) { s >> param; - if ((param.c_str()[0] == ':') && (item)) + if ((param != "") && (param != "\n")) { - char* str = (char*)param.c_str(); - str++; - param = str; - std::string append; - while (!s.eof()) + if ((param.c_str()[0] == ':') && (item)) { - append = ""; - s >> append; - if (append != "") + char* str = (char*)param.c_str(); + str++; + param = str; + std::string append; + while (!s.eof()) { - param = param + " " + append; + append = ""; + s >> append; + if (append != "") + { + param = param + " " + append; + } } } + item++; + n.push_back(param); } - item++; - n.push_back(param); } return n; } bool ProcessLine(std::string line) { - Srv->SendToModeMask("o",WM_AND,"inbound-line: '"+line+"'"); - - std::deque params = this->Split(line); + char* l = (char*)line.c_str(); + while ((strlen(l)) && (l[strlen(l)-1] == '\r') || (l[strlen(l)-1] == '\n')) + l[strlen(l)-1] = '\0'; + line = l; + if (line == "") + return true; + Srv->Log(DEBUG,"inbound-line: '"+line+"'"); + std::deque params = this->Split(line,true); std::string command = ""; std::string prefix = ""; if (((params[0].c_str())[0] == ':') && (params.size() > 1)) @@ -576,6 +933,12 @@ class TreeSocket : public InspSocket this->LinkState = CONNECTED; Node = new TreeServer(InboundServerName,InboundDescription,TreeRoot,this); TreeRoot->AddChild(Node); + params.clear(); + params.push_back(InboundServerName); + params.push_back("*"); + params.push_back("1"); + params.push_back(":"+InboundDescription); + DoOneToAllButSender(TreeRoot->GetName(),"SERVER",params,InboundServerName); this->DoBurst(Node); } else if (command == "ERROR") @@ -603,26 +966,115 @@ class TreeSocket : public InspSocket // This is the 'authenticated' state, when all passwords // have been exchanged and anything past this point is taken // as gospel. + std::string target = ""; if ((command == "NICK") && (params.size() > 1)) { return this->IntroduceClient(prefix,params); } + else if (command == "FJOIN") + { + return this->ForceJoin(prefix,params); + } + else if (command == "SERVER") + { + return this->RemoteServer(prefix,params); + } + else if (command == "OPERTYPE") + { + return this->OperType(prefix,params); + } + else if (command == "FMODE") + { + return this->ForceMode(prefix,params); + } + else if (command == "KILL") + { + return this->RemoteKill(prefix,params); + } + else if (command == "FTOPIC") + { + return this->ForceTopic(prefix,params); + } + else if (command == "REHASH") + { + return this->RemoteRehash(prefix,params); + } + else if (command == "SQUIT") + { + if (params.size() == 2) + { + this->Squit(FindServer(params[0]),params[1]); + } + return true; + } + else + { + // not a special inter-server command. + // Emulate the actual user doing the command, + // this saves us having a huge ugly parser. + userrec* who = Srv->FindNick(prefix); + std::string sourceserv = this->myhost; + if (this->InboundServerName != "") + { + sourceserv = this->InboundServerName; + } + if (who) + { + // its a user + target = who->server; + char* strparams[127]; + for (unsigned int q = 0; q < params.size(); q++) + { + strparams[q] = (char*)params[q].c_str(); + } + log(DEBUG,"*** CALL COMMAND HANDLER FOR %s, SOURCE: '%s'",command.c_str(),who->nick); + Srv->CallCommandHandler(command, strparams, params.size(), who); + } + else + { + // its not a user. Its either a server, or somethings screwed up. + if (IsServer(prefix)) + { + target = Srv->GetServerName(); + } + else + { + log(DEBUG,"Command with unknown origin '%s'",prefix.c_str()); + return true; + } + } + return DoOneToAllButSenderRaw(line,sourceserv); + + } return true; break; } return true; } - virtual void OnTimeout() + virtual void OnTimeout() { - if (this->LinkState = CONNECTING) + if (this->LinkState == CONNECTING) { - Srv->SendOpers("*** CONNECT: Connection to "+myhost+" timed out."); + Srv->SendOpers("*** CONNECT: Connection to \002"+myhost+"\002 timed out."); } } - virtual void OnClose() + virtual void OnClose() { + // Connection closed. + // If the connection is fully up (state CONNECTED) + // then propogate a netsplit to all peers. + std::string quitserver = this->myhost; + if (this->InboundServerName != "") + { + quitserver = this->InboundServerName; + } + TreeServer* s = FindServer(quitserver); + if (s) + { + Squit(s,"Remote host closed the connection"); + } } virtual int OnIncomingConnection(int newsock, char* ip) @@ -633,9 +1085,95 @@ class TreeSocket : public InspSocket } }; +bool DoOneToAllButSenderRaw(std::string data,std::string omit) +{ + for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++) + { + TreeServer* Route = TreeRoot->GetChild(x); + if ((Route->GetSocket()) && (Route->GetName() != omit) && (BestRouteTo(omit) != Route)) + { + TreeSocket* Sock = Route->GetSocket(); + log(DEBUG,"Sending RAW to %s",Route->GetName().c_str()); + Sock->WriteLine(data); + } + } + return true; +} + +bool DoOneToAllButSender(std::string prefix, std::string command, std::deque params, std::string omit) +{ + log(DEBUG,"ALLBUTONE: Comes from %s SHOULD NOT go back to %s",prefix.c_str(),omit.c_str()); + // TODO: Special stuff with privmsg and notice + std::string FullLine = ":" + prefix + " " + command; + for (unsigned int x = 0; x < params.size(); x++) + { + FullLine = FullLine + " " + params[x]; + } + for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++) + { + TreeServer* Route = TreeRoot->GetChild(x); + // Send the line IF: + // The route has a socket (its a direct connection) + // The route isnt the one to be omitted + // The route isnt the path to the one to be omitted + if ((Route->GetSocket()) && (Route->GetName() != omit) && (BestRouteTo(omit) != Route)) + { + TreeSocket* Sock = Route->GetSocket(); + log(DEBUG,"Sending to %s",Route->GetName().c_str()); + Sock->WriteLine(FullLine); + } + } + return true; +} + +bool DoOneToMany(std::string prefix, std::string command, std::deque params) +{ + std::string FullLine = ":" + prefix + " " + command; + for (unsigned int x = 0; x < params.size(); x++) + { + FullLine = FullLine + " " + params[x]; + } + for (unsigned int x = 0; x < TreeRoot->ChildCount(); x++) + { + TreeServer* Route = TreeRoot->GetChild(x); + if (Route->GetSocket()) + { + TreeSocket* Sock = Route->GetSocket(); + Sock->WriteLine(FullLine); + } + } + return true; +} + +bool DoOneToOne(std::string prefix, std::string command, std::deque params, std::string target) +{ + TreeServer* Route = BestRouteTo(target); + if (Route) + { + std::string FullLine = ":" + prefix + " " + command; + for (unsigned int x = 0; x < params.size(); x++) + { + FullLine = FullLine + " " + params[x]; + } + if (Route->GetSocket()) + { + TreeSocket* Sock = Route->GetSocket(); + Sock->WriteLine(FullLine); + } + return true; + } + else + { + log(DEBUG,"Could not route message with target %s: %s",target.c_str(),command.c_str()); + return true; + } +} + + class ModuleSpanningTree : public Module { std::vector Bindings; + int line; public: @@ -695,8 +1233,24 @@ class ModuleSpanningTree : public Module ReadConfiguration(true); } + void ShowLinks(TreeServer* Current, userrec* user, int hops) + { + std::string Parent = TreeRoot->GetName(); + if (Current->GetParent()) + { + Parent = Current->GetParent()->GetName(); + } + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + ShowLinks(Current->GetChild(q),user,hops+1); + } + WriteServ(user->fd,"364 %s %s %s :%d %s",user->nick,Current->GetName().c_str(),Parent.c_str(),hops,Current->GetDesc().c_str()); + } + void HandleLinks(char** parameters, int pcnt, userrec* user) { + ShowLinks(TreeRoot,user,0); + WriteServ(user->fd,"365 %s * :End of /LINKS list.",user->nick); return; } @@ -705,8 +1259,83 @@ class ModuleSpanningTree : public Module return; } + // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS. + + void ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][80]) + { + if (line < 128) + { + for (int t = 0; t < depth; t++) + { + matrix[line][t] = ' '; + } + strlcpy(&matrix[line][depth],Current->GetName().c_str(),80); + line++; + for (unsigned int q = 0; q < Current->ChildCount(); q++) + { + ShowMap(Current->GetChild(q),user,depth+2,matrix); + } + } + } + + // Ok, prepare to be confused. + // After much mulling over how to approach this, it struck me that + // the 'usual' way of doing a /MAP isnt the best way. Instead of + // keeping track of a ton of ascii characters, and line by line + // under recursion working out where to place them using multiplications + // and divisons, we instead render the map onto a backplane of characters + // (a character matrix), then draw the branches as a series of "L" shapes + // from the nodes. This is not only friendlier on CPU it uses less stack. + void HandleMap(char** parameters, int pcnt, userrec* user) { + // This array represents a virtual screen which we will + // "scratch" draw to, as the console device of an irc + // client does not provide for a proper terminal. + char matrix[128][80]; + for (unsigned int t = 0; t < 128; t++) + { + matrix[t][0] = '\0'; + } + line = 0; + // The only recursive bit is called here. + ShowMap(TreeRoot,user,0,matrix); + // Process each line one by one. The algorithm has a limit of + // 128 servers (which is far more than a spanning tree should have + // anyway, so we're ok). This limit can be raised simply by making + // the character matrix deeper, 128 rows taking 10k of memory. + for (int l = 1; l < line; l++) + { + // scan across the line looking for the start of the + // servername (the recursive part of the algorithm has placed + // the servers at indented positions depending on what they + // are related to) + int first_nonspace = 0; + while (matrix[l][first_nonspace] == ' ') + { + first_nonspace++; + } + first_nonspace--; + // Draw the `- (corner) section: this may be overwritten by + // another L shape passing along the same vertical pane, becoming + // a |- (branch) section instead. + matrix[l][first_nonspace] = '-'; + matrix[l][first_nonspace-1] = '`'; + int l2 = l - 1; + // Draw upwards until we hit the parent server, causing possibly + // other corners (`-) to become branches (|-) + while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`')) + { + matrix[l2][first_nonspace-1] = '|'; + l2--; + } + } + // dump the whole lot to the user. This is the easy bit, honest. + for (int t = 0; t < line; t++) + { + WriteServ(user->fd,"006 %s :%s",user->nick,&matrix[t][0]); + } + WriteServ(user->fd,"007 %s :End of /MAP",user->nick); return; } @@ -759,6 +1388,238 @@ class ModuleSpanningTree : public Module return 0; } + virtual void OnGetServerDescription(std::string servername,std::string &description) + { + TreeServer* s = FindServer(servername); + if (s) + { + description = s->GetDesc(); + } + } + + virtual void OnUserInvite(userrec* source,userrec* dest,chanrec* channel) + { + if (std::string(source->server) == Srv->GetServerName()) + { + std::deque params; + params.push_back(dest->nick); + params.push_back(channel->name); + DoOneToMany(source->nick,"INVITE",params); + } + } + + virtual void OnPostLocalTopicChange(userrec* user, chanrec* chan, std::string topic) + { + std::deque params; + params.push_back(chan->name); + params.push_back(":"+topic); + DoOneToMany(user->nick,"TOPIC",params); + } + + virtual void OnUserNotice(userrec* user, void* dest, int target_type, std::string text) + { + if (target_type == TYPE_USER) + { + userrec* d = (userrec*)dest; + if ((std::string(d->server) != Srv->GetServerName()) && (std::string(user->server) == Srv->GetServerName())) + { + std::deque params; + params.clear(); + params.push_back(d->nick); + params.push_back(":"+text); + DoOneToOne(user->nick,"NOTICE",params,d->server); + } + } + else + { + if (std::string(user->server) == Srv->GetServerName()) + { + chanrec *c = (chanrec*)dest; + std::deque params; + params.push_back(c->name); + params.push_back(":"+text); + DoOneToMany(user->nick,"NOTICE",params); + } + } + } + + virtual void OnUserMessage(userrec* user, void* dest, int target_type, std::string text) + { + if (target_type == TYPE_USER) + { + // route private messages which are targetted at clients only to the server + // which needs to receive them + userrec* d = (userrec*)dest; + if ((std::string(d->server) != Srv->GetServerName()) && (std::string(user->server) == Srv->GetServerName())) + { + std::deque params; + params.clear(); + params.push_back(d->nick); + params.push_back(":"+text); + DoOneToOne(user->nick,"PRIVMSG",params,d->server); + } + } + else + { + if (std::string(user->server) == Srv->GetServerName()) + { + chanrec *c = (chanrec*)dest; + std::deque params; + params.push_back(c->name); + params.push_back(":"+text); + DoOneToMany(user->nick,"PRIVMSG",params); + } + } + } + + virtual void OnUserJoin(userrec* user, chanrec* channel) + { + // Only do this for local users + if (std::string(user->server) == Srv->GetServerName()) + { + log(DEBUG,"**** User on %s JOINS %s",user->server,channel->name); + std::deque params; + params.clear(); + params.push_back(channel->name); + if (*channel->key) + { + log(DEBUG,"**** With key %s",channel->key); + // if the channel has a key, force the join by emulating the key. + params.push_back(channel->key); + } + DoOneToMany(user->nick,"JOIN",params); + } + } + + virtual void OnUserPart(userrec* user, chanrec* channel) + { + if (std::string(user->server) == Srv->GetServerName()) + { + log(DEBUG,"**** User on %s PARTS %s",user->server,channel->name); + std::deque params; + params.clear(); + params.push_back(channel->name); + DoOneToMany(user->nick,"PART",params); + } + } + + virtual void OnUserConnect(userrec* user) + { + char agestr[MAXBUF]; + if (std::string(user->server) == Srv->GetServerName()) + { + log(DEBUG,"**** User on %s CONNECTS: %s",user->server,user->nick); + std::deque 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); + params.push_back(user->dhost); + params.push_back(user->ident); + params.push_back("+"+std::string(user->modes)); + params.push_back(user->ip); + params.push_back(":"+std::string(user->fullname)); + DoOneToMany(Srv->GetServerName(),"NICK",params); + } + } + + virtual void OnUserQuit(userrec* user, std::string reason) + { + if (std::string(user->server) == Srv->GetServerName()) + { + log(DEBUG,"**** User on %s QUITS: %s",user->server,user->nick); + std::deque params; + params.push_back(":"+reason); + DoOneToMany(user->nick,"QUIT",params); + } + } + + virtual void OnUserPostNick(userrec* user, std::string oldnick) + { + if (std::string(user->server) == Srv->GetServerName()) + { + log(DEBUG,"**** User on %s changes NICK: %s",user->server,user->nick); + std::deque params; + params.push_back(user->nick); + DoOneToMany(oldnick,"NICK",params); + } + } + + virtual void OnUserKick(userrec* source, userrec* user, chanrec* chan, std::string reason) + { + if (std::string(source->server) == Srv->GetServerName()) + { + log(DEBUG,"**** User on %s KICKs: %s %s",source->server,source->nick,user->nick); + std::deque params; + params.push_back(chan->name); + params.push_back(user->nick); + params.push_back(":"+reason); + DoOneToMany(source->nick,"KICK",params); + } + } + + virtual void OnRemoteKill(userrec* source, userrec* dest, std::string reason) + { + std::deque params; + params.push_back(dest->nick); + params.push_back(":"+reason); + DoOneToMany(source->nick,"KILL",params); + } + + virtual void OnRehash(std::string parameter) + { + if (parameter != "") + { + std::deque params; + params.push_back(parameter); + DoOneToMany(Srv->GetServerName(),"REHASH",params); + // check for self + if (Srv->MatchText(Srv->GetServerName(),parameter)) + { + Srv->RehashServer(); + } + } + } + + // note: the protocol does not allow direct umode +o except + // via NICK with 8 params. sending OPERTYPE infers +o modechange + // locally. + virtual void OnOper(userrec* user, std::string opertype) + { + if (std::string(user->server) == Srv->GetServerName()) + { + std::deque params; + params.push_back(opertype); + DoOneToMany(user->nick,"OPERTYPE",params); + } + } + + virtual void OnMode(userrec* user, void* dest, int target_type, std::string text) + { + log(DEBUG,"*** ONMODE TRIGGER"); + if (std::string(user->server) == Srv->GetServerName()) + { + log(DEBUG,"*** LOCAL"); + if (target_type == TYPE_USER) + { + userrec* u = (userrec*)dest; + std::deque params; + params.push_back(u->nick); + params.push_back(text); + DoOneToMany(user->nick,"MODE",params); + } + else + { + chanrec* c = (chanrec*)dest; + std::deque params; + params.push_back(c->name); + params.push_back(text); + DoOneToMany(user->nick,"MODE",params); + } + } + } + virtual ~ModuleSpanningTree() { delete Srv;