enum ServerState { LISTENER, CONNECTING, WAIT_AUTH_1, WAIT_AUTH_2, CONNECTED };
typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, irc::StrHashComp> user_hash;
+typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, 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<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);
+
class TreeServer
{
TreeServer* Parent;
}
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<TreeServer*>::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
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.
}
}
+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;
ServerState LinkState;
std::string InboundServerName;
std::string InboundDescription;
+ int num_lost_users;
+ int num_lost_servers;
public:
}
}
+ 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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];
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();
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);
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)
{
// Send users and their channels
this->SendUsers(s);
// TODO: Send everything else (channel modes etc)
+ this->SendChannelModes(s);
this->WriteLine("ENDBURST");
}
return false;
}
+ bool OperType(std::string prefix, std::deque<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> params)
{
if (params.size() < 4)
// 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;
}
{
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,
return false;
}
- std::deque<std::string> Split(std::string line)
+ std::deque<std::string> Split(std::string line, bool stripcolon)
{
std::deque<std::string> n;
std::stringstream s(line);
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<std::string> 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<std::string> params = this->Split(line,true);
std::string command = "";
std::string prefix = "";
if (((params[0].c_str())[0] == ':') && (params.size() > 1))
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")
// 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)
}
};
+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<std::string> 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<std::string> 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<std::string> 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<TreeSocket*> Bindings;
+ int line;
public:
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;
}
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;
}
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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<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);
+ 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> params;
+ params.push_back(u->nick);
+ params.push_back(text);
+ DoOneToMany(user->nick,"MODE",params);
+ }
+ else
+ {
+ chanrec* c = (chanrec*)dest;
+ std::deque<std::string> params;
+ params.push_back(c->name);
+ params.push_back(text);
+ DoOneToMany(user->nick,"MODE",params);
+ }
+ }
+ }
+
virtual ~ModuleSpanningTree()
{
delete Srv;