+ // recursively send the server tree with distances as hops
+ void SendServers(TreeServer* Current, TreeServer* s, int hops)
+ {
+ char command[1024];
+ for (unsigned int q = 0; q < Current->ChildCount(); q++)
+ {
+ TreeServer* recursive_server = Current->GetChild(q);
+ if (recursive_server != s)
+ {
+ // :source.server SERVER server.name hops :Description
+ snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
+ this->WriteLine(command);
+ // down to next level
+ this->SendServers(recursive_server, s, hops+1);
+ }
+ }
+ }
+
+ 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 host = params[2];
+ std::string dhost = params[3];
+ 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();
+ log(DEBUG,"Introduce client %s!%s@%s",tempnick,ident.c_str(),host.c_str());
+
+ user_hash::iterator iter;
+ iter = clientlist.find(tempnick);
+ if (iter != clientlist.end())
+ {
+ // nick collision
+ 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);
+ strlcpy(clientlist[tempnick]->host, host.c_str(),160);
+ strlcpy(clientlist[tempnick]->dhost, dhost.c_str(),160);
+ clientlist[tempnick]->server = (char*)FindServerNamePtr(source.c_str());
+ strlcpy(clientlist[tempnick]->ident, ident.c_str(),IDENTMAX);
+ strlcpy(clientlist[tempnick]->fullname, gecos.c_str(),MAXGECOS);
+ clientlist[tempnick]->registered = 7;
+ clientlist[tempnick]->signon = age;
+ strlcpy(clientlist[tempnick]->ip,ip.c_str(),16);
+ for (int i = 0; i < MAXCHANS; i++)
+ {
+ 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)
+ {
+ char data[MAXBUF];
+ for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
+ {
+ snprintf(data,MAXBUF,":%s NICK %lu %s %s %s %s +%s %s :%s",u->second->server,(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->modes,u->second->ip,u->second->fullname);
+ this->WriteLine(data);
+ if (strchr(u->second->modes,'o'))
+ {
+ this->WriteLine(":"+std::string(u->second->nick)+" OPERTYPE "+std::string(u->second->oper));
+ }
+ char* chl = chlist(u->second,u->second);
+ if (*chl)
+ {
+ this->WriteLine(":"+std::string(u->second->nick)+" FJOIN "+std::string(chl));
+ }
+ }
+ }
+