]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/main.cpp
da0e61786e92d7febcdbe8f6def6edad0d14c84e
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / main.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $ModDesc: Provides a spanning tree server link protocol */
15
16 #include "configreader.h"
17 #include "users.h"
18 #include "channels.h"
19 #include "modules.h"
20 #include "commands/cmd_whois.h"
21 #include "commands/cmd_stats.h"
22 #include "socket.h"
23 #include "inspircd.h"
24 #include "wildcard.h"
25 #include "xline.h"
26 #include "transport.h"
27
28 #include "m_spanningtree/main.h"
29 #include "m_spanningtree/utils.h"
30 #include "m_spanningtree/treeserver.h"
31 #include "m_spanningtree/link.h"
32 #include "m_spanningtree/treesocket.h"
33
34 /** If you make a change which breaks the protocol, increment this.
35  * If you  completely change the protocol, completely change the number.
36  *
37  * IMPORTANT: If you make changes, document your changes here, without fail:
38  * http://www.inspircd.org/wiki/List_of_protocol_changes_between_versions
39  *
40  * Failure to document your protocol changes will result in a painfully
41  * painful death by pain. You have been warned.
42  */
43 const long ProtocolVersion = 1103;
44
45 /* Foward declarations */
46 class ModuleSpanningTree;
47
48 class HandshakeTimer : public InspTimer
49 {
50  private:
51         InspIRCd* Instance;
52         TreeSocket* sock;
53         Link* lnk;
54         SpanningTreeUtilities* Utils;
55         int thefd;
56  public:
57         HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u);
58         virtual void Tick(time_t TIME);
59 };
60
61
62 /** Handle /RCONNECT
63  */
64 class cmd_rconnect : public command_t
65 {
66         Module* Creator;
67         SpanningTreeUtilities* Utils;
68  public:
69         cmd_rconnect (InspIRCd* Instance, Module* Callback, SpanningTreeUtilities* Util) : command_t(Instance, "RCONNECT", 'o', 2), Creator(Callback), Utils(Util)
70         {
71                 this->source = "m_spanningtree.so";
72                 syntax = "<remote-server-mask> <target-server-mask>";
73         }
74
75         CmdResult Handle (const char** parameters, int pcnt, userrec *user)
76         {
77                 if (IS_LOCAL(user))
78                 {
79                         if (!Utils->FindServer(parameters[0]))
80                         {
81                                 user->WriteServ("NOTICE %s :*** RCONNECT: Server \002%s\002 isn't connected to the network!", user->nick, parameters[0]);
82                                 return CMD_FAILURE;
83                         }
84                         
85                         user->WriteServ("NOTICE %s :*** RCONNECT: Sending remote connect to \002%s\002 to connect server \002%s\002.",user->nick,parameters[0],parameters[1]);
86                 }
87                 
88                 /* Is this aimed at our server? */
89                 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameters[0]))
90                 {
91                         /* Yes, initiate the given connect */
92                         ServerInstance->SNO->WriteToSnoMask('l',"Remote CONNECT from %s matching \002%s\002, connecting server \002%s\002",user->nick,parameters[0],parameters[1]);
93                         const char* para[1];
94                         para[0] = parameters[1];
95                         std::string original_command = std::string("CONNECT ") + parameters[1];
96                         Creator->OnPreCommand("CONNECT", para, 1, user, true, original_command);
97                 }
98                 
99                 return CMD_SUCCESS;
100         }
101 };
102  
103
104 /** Because most of the I/O gubbins are encapsulated within
105  * InspSocket, we just call the superclass constructor for
106  * most of the action, and append a few of our own values
107  * to it.
108  */
109 TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod)
110         : InspSocket(SI, host, port, listening, maxtime), Utils(Util), Hook(HookMod)
111 {
112         myhost = host;
113         this->LinkState = LISTENER;
114         if (listening && Hook)
115                 InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
116 }
117
118 TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, std::string ServerName, Module* HookMod)
119         : InspSocket(SI, host, port, listening, maxtime), Utils(Util), Hook(HookMod)
120 {
121         myhost = ServerName;
122         this->LinkState = CONNECTING;
123         if (Hook)
124                 InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
125 }
126
127 /** When a listening socket gives us a new file descriptor,
128  * we must associate it with a socket without creating a new
129  * connection. This constructor is used for this purpose.
130  */
131 TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod)
132         : InspSocket(SI, newfd, ip), Utils(Util), Hook(HookMod)
133 {
134         this->LinkState = WAIT_AUTH_1;
135         /* If we have a transport module hooked to the parent, hook the same module to this
136          * socket, and set a timer waiting for handshake before we send CAPAB etc.
137          */
138         if (Hook)
139         {
140                 InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
141                 Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils));
142         }
143         else
144         {
145                 /* Otherwise, theres no lower layer transport in plain TCP/IP,
146                  * so just send the capabilities right now.
147                  */
148                 this->SendCapabilities();
149         }
150 }
151
152 ServerState TreeSocket::GetLinkState()
153 {
154         return this->LinkState;
155 }
156
157 Module* TreeSocket::GetHook()
158 {
159         return this->Hook;
160 }
161
162 TreeSocket::~TreeSocket()
163 {
164         if (Hook)
165                 InspSocketUnhookRequest(this, (Module*)Utils->Creator, Hook).Send();
166 }
167         
168 /** When an outbound connection finishes connecting, we receive
169  * this event, and must send our SERVER string to the other
170  * side. If the other side is happy, as outlined in the server
171  * to server docs on the inspircd.org site, the other side
172  * will then send back its own server string.
173  */
174 bool TreeSocket::OnConnected()
175 {
176         if (this->LinkState == CONNECTING)
177         {
178                 /* we do not need to change state here. */
179                 for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
180                 {
181                         if (x->Name == this->myhost)
182                         {
183                                 this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] started.");
184                                 if (Hook)
185                                 {
186                                         InspSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
187                                         this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+x->Hook+"\2");
188                                 }
189                                 else
190                                         this->SendCapabilities();
191                                 /* found who we're supposed to be connecting to, send the neccessary gubbins. */
192                                 if (Hook)
193                                         Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils));
194                                 else
195                                         this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+x->SendPass+" 0 :"+this->Instance->Config->ServerDesc);
196                                 return true;
197                         }
198                 }
199         }
200         /* There is a (remote) chance that between the /CONNECT and the connection
201          * being accepted, some muppet has removed the <link> block and rehashed.
202          * If that happens the connection hangs here until it's closed. Unlikely
203          * and rather harmless.
204          */
205         this->Instance->SNO->WriteToSnoMask('l',"Connection to \2"+myhost+"\2 lost link tag(!)");
206         return true;
207 }
208         
209 void TreeSocket::OnError(InspSocketError e)
210 {
211         /* We don't handle this method, because all our
212          * dirty work is done in OnClose() (see below)
213          * which is still called on error conditions too.
214          */
215         if (e == I_ERR_CONNECT)
216         {
217                 this->Instance->SNO->WriteToSnoMask('l',"Connection failed: Connection to \002"+myhost+"\002 refused");
218                 Link* MyLink = Utils->FindLink(myhost);
219                 if (MyLink)
220                         Utils->DoFailOver(MyLink);
221         }
222 }
223
224 int TreeSocket::OnDisconnect()
225 {
226         /* For the same reason as above, we don't
227          * handle OnDisconnect()
228          */
229         return true;
230 }
231
232 /** Recursively send the server tree with distances as hops.
233  * This is used during network burst to inform the other server
234  * (and any of ITS servers too) of what servers we know about.
235  * If at any point any of these servers already exist on the other
236  * end, our connection may be terminated. The hopcounts given
237  * by this function are relative, this doesn't matter so long as
238  * they are all >1, as all the remote servers re-calculate them
239  * to be relative too, with themselves as hop 0.
240  */
241 void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
242 {
243         char command[1024];
244         for (unsigned int q = 0; q < Current->ChildCount(); q++)
245         {
246                 TreeServer* recursive_server = Current->GetChild(q);
247                 if (recursive_server != s)
248                 {
249                         snprintf(command,1024,":%s SERVER %s * %d :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,recursive_server->GetDesc().c_str());
250                         this->WriteLine(command);
251                         this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion());
252                         /* down to next level */
253                         this->SendServers(recursive_server, s, hops+1);
254                 }
255         }
256 }
257
258 std::string TreeSocket::MyCapabilities()
259 {
260         std::vector<std::string> modlist;
261         std::string capabilities = "";
262         for (int i = 0; i <= this->Instance->GetModuleCount(); i++)
263         {
264                 if (this->Instance->modules[i]->GetVersion().Flags & VF_COMMON)
265                         modlist.push_back(this->Instance->Config->module_names[i]);
266         }
267         sort(modlist.begin(),modlist.end());
268         for (unsigned int i = 0; i < modlist.size(); i++)
269         {
270                 if (i)
271                         capabilities = capabilities + ",";
272                 capabilities = capabilities + modlist[i];
273         }
274         return capabilities;
275 }
276         
277 void TreeSocket::SendCapabilities()
278 {
279         irc::commasepstream modulelist(MyCapabilities());
280         this->WriteLine("CAPAB START");
281
282         /* Send module names, split at 509 length */
283         std::string item = "*";
284         std::string line = "CAPAB MODULES ";
285         while ((item = modulelist.GetToken()) != "")
286         {
287                 if (line.length() + item.length() + 1 > 509)
288                 {
289                         this->WriteLine(line);
290                         line = "CAPAB MODULES ";
291                 }
292
293                 if (line != "CAPAB MODULES ")
294                         line.append(",");
295
296                 line.append(item);
297         }
298         if (line != "CAPAB MODULES ")
299                 this->WriteLine(line);
300
301         int ip6 = 0;
302         int ip6support = 0;
303 #ifdef IPV6
304         ip6 = 1;
305 #endif
306 #ifdef SUPPORT_IP6LINKS
307         ip6support = 1;
308 #endif
309         this->WriteLine("CAPAB CAPABILITIES :NICKMAX="+ConvToStr(NICKMAX)+" HALFOP="+ConvToStr(this->Instance->Config->AllowHalfop)+" CHANMAX="+ConvToStr(CHANMAX)+" MAXMODES="+ConvToStr(MAXMODES)+" IDENTMAX="+ConvToStr(IDENTMAX)+" MAXQUIT="+ConvToStr(MAXQUIT)+" MAXTOPIC="+ConvToStr(MAXTOPIC)+" MAXKICK="+ConvToStr(MAXKICK)+" MAXGECOS="+ConvToStr(MAXGECOS)+" MAXAWAY="+ConvToStr(MAXAWAY)+" IP6NATIVE="+ConvToStr(ip6)+" IP6SUPPORT="+ConvToStr(ip6support)+" PROTOCOL="+ConvToStr(ProtocolVersion));
310
311         this->WriteLine("CAPAB END");
312 }
313
314 /* Check a comma seperated list for an item */
315 bool TreeSocket::HasItem(const std::string &list, const std::string &item)
316 {
317         irc::commasepstream seplist(list);
318         std::string item2 = "*";
319         while ((item2 = seplist.GetToken()) != "")
320         {
321                 if (item2 == item)
322                         return true;
323         }
324         return false;
325 }
326
327 /* Isolate and return the elements that are different between two comma seperated lists */
328 std::string TreeSocket::ListDifference(const std::string &one, const std::string &two)
329 {
330         irc::commasepstream list_one(one);
331         std::string item = "*";
332         std::string result = "";
333         while ((item = list_one.GetToken()) != "")
334         {
335                 if (!HasItem(two, item))
336                 {
337                         result.append(" ");
338                         result.append(item);
339                 }
340         }
341         return result;
342 }
343
344 bool TreeSocket::Capab(const std::deque<std::string> &params)
345 {
346         if (params.size() < 1)
347         {
348                 this->WriteLine("ERROR :Invalid number of parameters for CAPAB - Mismatched version");
349                 return false;
350         }
351         if (params[0] == "START")
352         {
353                 this->ModuleList = "";
354                 this->CapKeys.clear();
355         }
356         else if (params[0] == "END")
357         {
358                 std::string reason = "";
359                 int ip6support = 0;
360 #ifdef SUPPORT_IP6LINKS
361                 ip6support = 1;
362 #endif
363                 /* Compare ModuleList and check CapKeys...
364                  * Maybe this could be tidier? -- Brain
365                  */
366                 if ((this->ModuleList != this->MyCapabilities()) && (this->ModuleList.length()))
367                 {
368                         std::string diff = ListDifference(this->ModuleList, this->MyCapabilities());
369                         if (!diff.length())
370                         {
371                                 diff = "your server:" + ListDifference(this->MyCapabilities(), this->ModuleList);
372                         }
373                         else
374                         {
375                                 diff = "this server:" + diff;
376                         }
377                         if (diff.length() == 12)
378                                 reason = "Module list in CAPAB is not alphabetically ordered, cannot compare lists.";
379                         else
380                                 reason = "Modules loaded on these servers are not correctly matched, these modules are not loaded on " + diff;
381                 }
382                 if (((this->CapKeys.find("IP6SUPPORT") == this->CapKeys.end()) && (ip6support)) || ((this->CapKeys.find("IP6SUPPORT") != this->CapKeys.end()) && (this->CapKeys.find("IP6SUPPORT")->second != ConvToStr(ip6support))))
383                         reason = "We don't both support linking to IPV6 servers";
384                 if (((this->CapKeys.find("IP6NATIVE") != this->CapKeys.end()) && (this->CapKeys.find("IP6NATIVE")->second == "1")) && (!ip6support))
385                         reason = "The remote server is IPV6 native, and we don't support linking to IPV6 servers";
386                 if (((this->CapKeys.find("NICKMAX") == this->CapKeys.end()) || ((this->CapKeys.find("NICKMAX") != this->CapKeys.end()) && (this->CapKeys.find("NICKMAX")->second != ConvToStr(NICKMAX)))))
387                         reason = "Maximum nickname lengths differ or remote nickname length not specified";
388                 if (((this->CapKeys.find("PROTOCOL") == this->CapKeys.end()) || ((this->CapKeys.find("PROTOCOL") != this->CapKeys.end()) && (this->CapKeys.find("PROTOCOL")->second != ConvToStr(ProtocolVersion)))))
389                 {
390                         if (this->CapKeys.find("PROTOCOL") != this->CapKeys.end())
391                         {
392                                 reason = "Mismatched protocol versions "+this->CapKeys.find("PROTOCOL")->second+" and "+ConvToStr(ProtocolVersion);
393                         }
394                         else
395                         {
396                                 reason = "Protocol version not specified";
397                         }
398                 }
399                 if (((this->CapKeys.find("HALFOP") == this->CapKeys.end()) && (Instance->Config->AllowHalfop)) || ((this->CapKeys.find("HALFOP") != this->CapKeys.end()) && (this->CapKeys.find("HALFOP")->second != ConvToStr(Instance->Config->AllowHalfop))))
400                         reason = "We don't both have halfop support enabled/disabled identically";
401                 if (((this->CapKeys.find("IDENTMAX") == this->CapKeys.end()) || ((this->CapKeys.find("IDENTMAX") != this->CapKeys.end()) && (this->CapKeys.find("IDENTMAX")->second != ConvToStr(IDENTMAX)))))
402                         reason = "Maximum ident lengths differ or remote ident length not specified";
403                 if (((this->CapKeys.find("CHANMAX") == this->CapKeys.end()) || ((this->CapKeys.find("CHANMAX") != this->CapKeys.end()) && (this->CapKeys.find("CHANMAX")->second != ConvToStr(CHANMAX)))))
404                         reason = "Maximum channel lengths differ or remote channel length not specified";
405                 if (((this->CapKeys.find("MAXMODES") == this->CapKeys.end()) || ((this->CapKeys.find("MAXMODES") != this->CapKeys.end()) && (this->CapKeys.find("MAXMODES")->second != ConvToStr(MAXMODES)))))
406                         reason = "Maximum modes per line differ or remote modes per line not specified";
407                 if (((this->CapKeys.find("MAXQUIT") == this->CapKeys.end()) || ((this->CapKeys.find("MAXQUIT") != this->CapKeys.end()) && (this->CapKeys.find("MAXQUIT")->second != ConvToStr(MAXQUIT)))))
408                         reason = "Maximum quit lengths differ or remote quit length not specified";
409                 if (((this->CapKeys.find("MAXTOPIC") == this->CapKeys.end()) || ((this->CapKeys.find("MAXTOPIC") != this->CapKeys.end()) && (this->CapKeys.find("MAXTOPIC")->second != ConvToStr(MAXTOPIC)))))
410                         reason = "Maximum topic lengths differ or remote topic length not specified";
411                 if (((this->CapKeys.find("MAXKICK") == this->CapKeys.end()) || ((this->CapKeys.find("MAXKICK") != this->CapKeys.end()) && (this->CapKeys.find("MAXKICK")->second != ConvToStr(MAXKICK)))))
412                         reason = "Maximum kick lengths differ or remote kick length not specified";
413                 if (((this->CapKeys.find("MAXGECOS") == this->CapKeys.end()) || ((this->CapKeys.find("MAXGECOS") != this->CapKeys.end()) && (this->CapKeys.find("MAXGECOS")->second != ConvToStr(MAXGECOS)))))
414                         reason = "Maximum GECOS (fullname) lengths differ or remote GECOS length not specified";
415                 if (((this->CapKeys.find("MAXAWAY") == this->CapKeys.end()) || ((this->CapKeys.find("MAXAWAY") != this->CapKeys.end()) && (this->CapKeys.find("MAXAWAY")->second != ConvToStr(MAXAWAY)))))
416                         reason = "Maximum awaymessage lengths differ or remote awaymessage length not specified";
417                 if (reason.length())
418                 {
419                         this->WriteLine("ERROR :CAPAB negotiation failed: "+reason);
420                         return false;
421                 }
422         }
423         else if ((params[0] == "MODULES") && (params.size() == 2))
424         {
425                 if (!this->ModuleList.length())
426                 {
427                         this->ModuleList.append(params[1]);
428                 }
429                 else
430                 {
431                         this->ModuleList.append(",");
432                         this->ModuleList.append(params[1]);
433                 }
434         }
435         else if ((params[0] == "CAPABILITIES") && (params.size() == 2))
436         {
437                 irc::tokenstream capabs(params[1]);
438                 std::string item = "*";
439                 while ((item = capabs.GetToken()) != "")
440                 {
441                         /* Process each key/value pair */
442                         std::string::size_type equals = item.rfind('=');
443                         if (equals != std::string::npos)
444                         {
445                                 std::string var = item.substr(0, equals);
446                                 std::string value = item.substr(equals+1, item.length());
447                                 CapKeys[var] = value;
448                         }
449                 }
450         }
451         return true;
452 }
453
454 /** This function forces this server to quit, removing this server
455  * and any users on it (and servers and users below that, etc etc).
456  * It's very slow and pretty clunky, but luckily unless your network
457  * is having a REAL bad hair day, this function shouldnt be called
458  * too many times a month ;-)
459  */
460 void TreeSocket::SquitServer(std::string &from, TreeServer* Current)
461 {
462         /* recursively squit the servers attached to 'Current'.
463          * We're going backwards so we don't remove users
464          * while we still need them ;)
465          */
466         for (unsigned int q = 0; q < Current->ChildCount(); q++)
467         {
468                 TreeServer* recursive_server = Current->GetChild(q);
469                 this->SquitServer(from,recursive_server);
470         }
471         /* Now we've whacked the kids, whack self */
472         num_lost_servers++;
473         num_lost_users += Current->QuitUsers(from);
474 }
475
476 /** This is a wrapper function for SquitServer above, which
477  * does some validation first and passes on the SQUIT to all
478  * other remaining servers.
479  */
480 void TreeSocket::Squit(TreeServer* Current, const std::string &reason)
481 {
482         if ((Current) && (Current != Utils->TreeRoot))
483         {
484                 Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server");
485                 rmode.Send(Instance);
486
487                 std::deque<std::string> params;
488                 params.push_back(Current->GetName());
489                 params.push_back(":"+reason);
490                 Utils->DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName());
491                 if (Current->GetParent() == Utils->TreeRoot)
492                 {
493                         this->Instance->WriteOpers("Server \002"+Current->GetName()+"\002 split: "+reason);
494                 }
495                 else
496                 {
497                         this->Instance->WriteOpers("Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
498                 }
499                 num_lost_servers = 0;
500                 num_lost_users = 0;
501                 std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
502                 SquitServer(from, Current);
503                 Current->Tidy();
504                 Current->GetParent()->DelChild(Current);
505                 DELETE(Current);
506                 this->Instance->WriteOpers("Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers);
507         }
508         else
509         {
510                 Instance->Log(DEFAULT,"Squit from unknown server");
511         }
512 }
513
514 /** FMODE command - server mode with timestamp checks */
515 bool TreeSocket::ForceMode(const std::string &source, std::deque<std::string> &params)
516 {
517         /* Chances are this is a 1.0 FMODE without TS */
518         if (params.size() < 3)
519         {
520                 /* No modes were in the command, probably a channel with no modes set on it */
521                 return true;
522         }
523         
524         bool smode = false;
525         std::string sourceserv;
526         /* Are we dealing with an FMODE from a user, or from a server? */
527         userrec* who = this->Instance->FindNick(source);
528         if (who)
529         {
530                 /* FMODE from a user, set sourceserv to the users server name */
531                 sourceserv = who->server;
532         }
533         else
534         {
535                 /* FMODE from a server, create a fake user to receive mode feedback */
536                 who = new userrec(this->Instance);
537                 who->SetFd(FD_MAGIC_NUMBER);
538                 smode = true;           /* Setting this flag tells us we should free the userrec later */
539                 sourceserv = source;    /* Set sourceserv to the actual source string */
540         }
541         const char* modelist[64];
542         time_t TS = 0;
543         int n = 0;
544         memset(&modelist,0,sizeof(modelist));
545         for (unsigned int q = 0; (q < params.size()) && (q < 64); q++)
546         {
547                 if (q == 1)
548                 {
549                         /* The timestamp is in this position.
550                          * We don't want to pass that up to the
551                          * server->client protocol!
552                          */
553                         TS = atoi(params[q].c_str());
554                 }
555                 else
556                 {
557                         /* Everything else is fine to append to the modelist */
558                         modelist[n++] = params[q].c_str();
559                 }
560                         
561         }
562         /* Extract the TS value of the object, either userrec or chanrec */
563         userrec* dst = this->Instance->FindNick(params[0]);
564         chanrec* chan = NULL;
565         time_t ourTS = 0;
566         if (dst)
567         {
568                 ourTS = dst->age;
569         }
570         else
571         {
572                 chan = this->Instance->FindChan(params[0]);
573                 if (chan)
574                 {
575                         ourTS = chan->age;
576                 }
577                 else
578                         /* Oops, channel doesnt exist! */
579                         return true;
580         }
581
582         /* TS is equal: Merge the mode changes, use voooodoooooo on modes
583          * with parameters.
584          */
585         if (TS == ourTS)
586         {
587                 ModeHandler* mh = NULL;
588                 unsigned long paramptr = 3;
589                 std::string to_bounce = "";
590                 std::string to_keep = "";
591                 std::vector<std::string> params_to_keep;
592                 std::string params_to_bounce = "";
593                 bool adding = true;
594                 char cur_change = 1;
595                 char old_change = 0;
596                 char old_bounce_change = 0;
597                 /* Merge modes, basically do special stuff to mode with params */
598                 for (std::string::iterator x = params[2].begin(); x != params[2].end(); x++)
599                 {
600                         switch (*x)
601                         {
602                                 case '-':
603                                         adding = false;
604                                 break;
605                                 case '+':
606                                         adding = true;
607                                 break;
608                                 default:
609                                         if (adding)
610                                         {
611                                                 /* We only care about whats being set,
612                                                  * not whats being unset
613                                                  */
614                                                 mh = this->Instance->Modes->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER);
615                                                 if ((mh) && (mh->GetNumParams(adding) > 0) && (!mh->IsListMode()))
616                                                 {
617                                                         /* We only want to do special things to
618                                                          * modes with parameters, we are going to rewrite
619                                                          * those parameters
620                                                          */
621                                                         ModePair ret;
622                                                         adding ? cur_change = '+' : cur_change = '-';
623                                                         ret = mh->ModeSet(smode ? NULL : who, dst, chan, params[paramptr]);
624                                                         /* The mode is set here, check which we should keep */
625                                                         if (ret.first)
626                                                         {
627                                                                 bool which_to_keep = mh->CheckTimeStamp(TS, ourTS, params[paramptr], ret.second, chan);
628                                                                 if (which_to_keep == true)
629                                                                 {
630                                                                         /* Keep ours, bounce theirs:
631                                                                          * Send back ours to them and
632                                                                          * drop their mode changs
633                                                                          */
634                                                                         adding ? cur_change = '+' : cur_change = '-';
635                                                                         if (cur_change != old_bounce_change)
636                                                                                 to_bounce += cur_change;
637                                                                         to_bounce += *x;
638                                                                         old_bounce_change = cur_change;
639                                                                         if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size()))
640                                                                                 params_to_bounce.append(" ").append(ret.second);
641                                                                 }
642                                                                 else
643                                                                 {
644                                                                         /* Keep theirs: Accept their mode change,
645                                                                          * do nothing else
646                                                                          */
647                                                                         adding ? cur_change = '+' : cur_change = '-';
648                                                                         if (cur_change != old_change)
649                                                                                 to_keep += cur_change;
650                                                                         to_keep += *x;
651                                                                         old_change = cur_change;
652                                                                         if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size()))
653                                                                                 params_to_keep.push_back(params[paramptr]);
654                                                                 }
655                                                         }
656                                                         else
657                                                         {
658                                                                 /* Mode isnt set here, we want it */
659                                                                 adding ? cur_change = '+' : cur_change = '-';
660                                                                 if (cur_change != old_change)
661                                                                         to_keep += cur_change;
662                                                                 to_keep += *x;
663                                                                 old_change = cur_change;
664                                                                 if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size()))
665                                                                         params_to_keep.push_back(params[paramptr]);
666                                                         }
667                                                         paramptr++;
668                                                 }
669                                                 else
670                                                 {
671                                                         mh = this->Instance->Modes->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER);
672                                                         if (mh)
673                                                         {
674                                                                 adding ? cur_change = '+' : cur_change = '-';
675
676                                                                 /* Just keep this, safe to merge with no checks
677                                                                  * it has no parameters
678                                                                  */
679
680                                                                 if (cur_change != old_change)
681                                                                         to_keep += cur_change;
682                                                                 to_keep += *x;
683                                                                 old_change = cur_change;
684
685                                                                 if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size()))
686                                                                 {
687                                                                         params_to_keep.push_back(params[paramptr++]);
688                                                                 }
689                                                         }
690                                                 }
691                                         }
692                                         else
693                                         {
694                                                 mh = this->Instance->Modes->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER);
695                                                 if (mh)
696                                                 {
697                                                         /* Taking a mode away */
698                                                         adding ? cur_change = '+' : cur_change = '-';
699                                                         if (cur_change != old_change)
700                                                                 to_keep += cur_change;
701                                                         to_keep += *x;
702                                                         old_change = cur_change;
703                                                         if ((mh->GetNumParams(adding) > 0) && (paramptr < params.size()))
704                                                                 params_to_keep.push_back(params[paramptr++]);
705                                                 }
706                                         }
707                                 break;
708                         }
709                 }
710                 if (to_bounce.length())
711                 {
712                         std::deque<std::string> newparams;
713                         newparams.push_back(params[0]);
714                         newparams.push_back(ConvToStr(ourTS));
715                         newparams.push_back(to_bounce+params_to_bounce);
716                         Utils->DoOneToOne(this->Instance->Config->ServerName,"FMODE",newparams,sourceserv);
717                 }
718                 if (to_keep.length())
719                 {
720                         unsigned int n = 2;
721                         unsigned int q = 0;
722                         modelist[0] = params[0].c_str();
723                         modelist[1] = to_keep.c_str();
724                         if (params_to_keep.size() > 0)
725                         {
726                                 for (q = 0; (q < params_to_keep.size()) && (q < 64); q++)
727                                 {
728                                         modelist[n++] = params_to_keep[q].c_str();
729                                 }
730                         }
731                         if (smode)
732                         {
733                                 this->Instance->SendMode(modelist, n, who);
734                         }
735                         else
736                         {
737                                 this->Instance->CallCommandHandler("MODE", modelist, n, who);
738                         }
739                         /* HOT POTATO! PASS IT ON! */
740                         Utils->DoOneToAllButSender(source,"FMODE",params,sourceserv);
741                 }
742         }
743         else
744         /* U-lined servers always win regardless of their TS */
745         if ((TS > ourTS) && (!this->Instance->ULine(source.c_str())))
746         {
747                 /* Bounce the mode back to its sender.* We use our lower TS, so the other end
748                  * SHOULD accept it, if its clock is right.
749                  *
750                  * NOTE: We should check that we arent bouncing anything thats already set at this end.
751                  * If we are, bounce +ourmode to 'reinforce' it. This prevents desyncs.
752                  * e.g. They send +l 50, we have +l 10 set. rather than bounce -l 50, we bounce +l 10.
753                  *
754                  * Thanks to jilles for pointing out this one-hell-of-an-issue before i even finished
755                  * writing the code. It took me a while to come up with this solution.
756                  *
757                  * XXX: BE SURE YOU UNDERSTAND THIS CODE FULLY BEFORE YOU MESS WITH IT.
758                  */
759                 std::deque<std::string> newparams;      /* New parameter list we send back */
760                 newparams.push_back(params[0]);         /* Target, user or channel */
761                 newparams.push_back(ConvToStr(ourTS));  /* Timestamp value of the target */
762                 newparams.push_back("");                /* This contains the mode string. For now
763                                                          * it's empty, we fill it below.
764                                                          */
765                 /* Intelligent mode bouncing. Don't just invert, reinforce any modes which are already
766                  * set to avoid a desync here.
767                  */
768                 std::string modebounce = "";
769                 bool adding = true;
770                 unsigned int t = 3;
771                 ModeHandler* mh = NULL;
772                 char cur_change = 1;
773                 char old_change = 0;
774                 for (std::string::iterator x = params[2].begin(); x != params[2].end(); x++)
775                 {
776                         /* Iterate over all mode chars in the sent set */
777                         switch (*x)
778                         {
779                                 /* Adding or subtracting modes? */
780                                 case '-':
781                                         adding = false;
782                                 break;
783                                 case '+':
784                                         adding = true;
785                                 break;
786                                 default:
787                                         /* Find the mode handler for this mode */
788                                         mh = this->Instance->Modes->FindMode(*x, chan ? MODETYPE_CHANNEL : MODETYPE_USER);
789                                         /* Got a mode handler?
790                                          * This also prevents us bouncing modes we have no handler for.
791                                          */
792                                         if (mh)
793                                         {
794                                                 ModePair ret;
795                                                 std::string p = "";
796                                                 /* Does the mode require a parameter right now?
797                                                  * If it does, fetch it if we can
798                                                  */
799                                                 if ((mh->GetNumParams(adding) > 0) && (t < params.size()))
800                                                         p = params[t++];
801                                                 /* Call the ModeSet method to determine if its set with the
802                                                  * given parameter here or not.
803                                                  */
804                                                 ret = mh->ModeSet(smode ? NULL : who, dst, chan, p);
805                                                 /* XXX: Really. Dont ask.
806                                                  * Determine from if its set combined with what the current
807                                                  * 'state' is (adding or not) as to wether we should 'invert'
808                                                  * or 'reinforce' the mode change
809                                                  */
810                                                 (!ret.first ? (adding ? cur_change = '-' : cur_change = '+') : (!adding ? cur_change = '-' : cur_change = '+'));
811                                                 /* Quickly determine if we have 'flipped' from + to -,
812                                                  * or - to +, to prevent unneccessary +/- chars in the
813                                                  * output string that waste bandwidth
814                                                  */
815                                                 if (cur_change != old_change)
816                                                         modebounce += cur_change;
817                                                 old_change = cur_change;
818                                                 /* Add the mode character to the output string */
819                                                 modebounce += mh->GetModeChar();
820                                                 /* We got a parameter back from ModeHandler::ModeSet,
821                                                  * are we supposed to be sending one out right now?
822                                                  */
823                                                 if (ret.second.length())
824                                                 {
825                                                         if (mh->GetNumParams(cur_change == '+') > 0)
826                                                                 /* Yes we're supposed to be sending out
827                                                                  * the parameter. Make sure it goes
828                                                                  */
829                                                                 newparams.push_back(ret.second);
830                                                 }
831                                         }
832                                 break;
833                         }
834                 }
835                 
836                 /* Update the parameters for FMODE with the new 'bounced' string */
837                 newparams[2] = modebounce;
838                 /* Only send it back the way it came, no need to send it anywhere else */
839                 Utils->DoOneToOne(this->Instance->Config->ServerName,"FMODE",newparams,sourceserv);
840         }
841         else
842         {
843                 /* The server was ulined, but something iffy is up with the TS.
844                  * Sound the alarm bells!
845                  */
846                 if ((this->Instance->ULine(sourceserv.c_str())) && (TS > ourTS))
847                 {
848                         this->Instance->WriteOpers("\2WARNING!\2 U-Lined server '%s' has bad TS for '%s' (accepted change): \2SYNC YOUR CLOCKS\2 to avoid this notice",sourceserv.c_str(),params[0].c_str());
849                 }
850                 /* Allow the mode, route it to either server or user command handling */
851                 if (smode)
852                         this->Instance->SendMode(modelist,n,who);
853                 else
854                         this->Instance->CallCommandHandler("MODE", modelist, n, who);
855                 /* HOT POTATO! PASS IT ON! */
856                 Utils->DoOneToAllButSender(source,"FMODE",params,sourceserv);
857         }
858         /* Are we supposed to free the userrec? */
859         if (smode)
860                 DELETE(who);
861
862         return true;
863 }
864
865 /** FTOPIC command */
866 bool TreeSocket::ForceTopic(const std::string &source, std::deque<std::string> &params)
867 {
868         if (params.size() != 4)
869                 return true;
870         time_t ts = atoi(params[1].c_str());
871         std::string nsource = source;
872         chanrec* c = this->Instance->FindChan(params[0]);
873         if (c)
874         {
875                 if ((ts >= c->topicset) || (!*c->topic))
876                 {
877                         std::string oldtopic = c->topic;
878                         strlcpy(c->topic,params[3].c_str(),MAXTOPIC);
879                         strlcpy(c->setby,params[2].c_str(),NICKMAX-1);
880                         c->topicset = ts;
881                         /* if the topic text is the same as the current topic,
882                          * dont bother to send the TOPIC command out, just silently
883                          * update the set time and set nick.
884                          */
885                         if (oldtopic != params[3])
886                         {
887                                 userrec* user = this->Instance->FindNick(source);
888                                 if (!user)
889                                 {
890                                         c->WriteChannelWithServ(source.c_str(), "TOPIC %s :%s", c->name, c->topic);
891                                 }
892                                 else
893                                 {
894                                         c->WriteChannel(user, "TOPIC %s :%s", c->name, c->topic);
895                                         nsource = user->server;
896                                 }
897                                 /* all done, send it on its way */
898                                 params[3] = ":" + params[3];
899                                 Utils->DoOneToAllButSender(source,"FTOPIC",params,nsource);
900                         }
901                 }
902                 
903         }
904         return true;
905 }
906
907 /** FJOIN, similar to TS6 SJOIN, but not quite. */
908 bool TreeSocket::ForceJoin(const std::string &source, std::deque<std::string> &params)
909 {
910         /* 1.1 FJOIN works as follows:
911          *
912          * Each FJOIN is sent along with a timestamp, and the side with the lowest
913          * timestamp 'wins'. From this point on we will refer to this side as the
914          * winner. The side with the higher timestamp loses, from this point on we
915          * will call this side the loser or losing side. This should be familiar to
916          * anyone who's dealt with dreamforge or TS6 before.
917          *
918          * When two sides of a split heal and this occurs, the following things
919          * will happen:
920          *
921          * If the timestamps are exactly equal, both sides merge their privilages
922          * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been
923          * re-created during a split, this is safe to do.
924          *
925          *
926          * If the timestamps are NOT equal, the losing side removes all privilage
927          * modes from all of its users that currently exist in the channel, before
928          * introducing new users into the channel which are listed in the FJOIN
929          * command's parameters. This means, all modes +ohv, and privilages added
930          * by modules, such as +qa. The losing side then LOWERS its timestamp value
931          * of the channel to match that of the winning side, and the modes of the
932          * users of the winning side are merged in with the losing side. The loser
933          * then sends out a set of FMODE commands which 'confirm' that it just
934          * removed all privilage modes from its existing users, which allows for
935          * services packages to still work correctly without needing to know the
936          * timestamping rules which InspIRCd follows. In TS6 servers this is always
937          * a problem, and services packages must contain code which explicitly
938          * behaves as TS6 does, removing ops from the losing side of a split where
939          * neccessary within its internal records, as this state information is
940          * not explicitly echoed out in that protocol.
941          *
942          * The winning side on the other hand will ignore all user modes from the
943          * losing side, so only its own modes get applied. Life is simple for those
944          * who succeed at internets. :-)
945          *
946          * NOTE: Unlike TS6 and dreamforge and other protocols which have SJOIN,
947          * FJOIN does not contain the simple-modes such as +iklmnsp. Why not,
948          * you ask? Well, quite simply because we don't need to. They'll be sent
949          * after the FJOIN by FMODE, and FMODE is timestamped, so in the event
950          * the losing side sends any modes for the channel which shouldnt win,
951          * they wont as their timestamp will be too high :-)
952          */
953
954         if (params.size() < 3)
955                 return true;
956
957         char first[MAXBUF];             /* The first parameter of the mode command */
958         char modestring[MAXBUF];        /* The mode sequence (2nd parameter) of the mode command */
959         char* mode_users[127];          /* The values used by the mode command */
960         memset(&mode_users,0,sizeof(mode_users));       /* Initialize mode parameters */
961         mode_users[0] = first;          /* Set this up to be our on-stack value */
962         mode_users[1] = modestring;     /* Same here as above */
963         strcpy(modestring,"+");         /* Initialize the mode sequence to just '+' */
964         unsigned int modectr = 2;       /* Pointer to the third mode parameter (e.g. the one after the +-sequence) */
965         
966         userrec* who = NULL;                    /* User we are currently checking */
967         std::string channel = params[0];        /* Channel name, as a string */
968         time_t TS = atoi(params[1].c_str());    /* Timestamp given to us for remote side */
969         bool created = false;
970                 
971         /* Try and find the channel */
972         chanrec* chan = this->Instance->FindChan(channel);
973
974         /* Initialize channel name in the mode parameters */
975         strlcpy(mode_users[0],channel.c_str(),MAXBUF);
976
977         /* default TS is a high value, which if we dont have this
978          * channel will let the other side apply their modes.
979          */
980         time_t ourTS = Instance->Time(true)+600;
981         /* Does this channel exist? if it does, get its REAL timestamp */
982         if (chan)
983                 ourTS = chan->age;
984         else
985                 created = true; /* don't perform deops, and set TS to correct time after processing. */
986         /* In 1.1, if they have the newer channel, we immediately clear
987          * all status modes from our users. We then accept their modes.
988          * If WE have the newer channel its the other side's job to do this.
989          * Note that this causes the losing server to send out confirming
990          * FMODE lines.
991          */
992         if (ourTS > TS)
993         {
994                 std::deque<std::string> param_list;
995                 /* Lower the TS here */
996                 if (Utils->AnnounceTSChange && chan)
997                         chan->WriteChannelWithServ(Instance->Config->ServerName,
998                         "NOTICE %s :TS for %s changed from %lu to %lu", chan->name, chan->name, ourTS, TS);
999                 ourTS = TS;
1000                 /* Zap all the privilage modes on our side, if the channel exists here */
1001                 if (!created)
1002                 {
1003                         param_list.push_back(channel);
1004                         /* Do this first! */
1005                         chan->age = TS;
1006                         this->RemoveStatus(Instance->Config->ServerName, param_list);
1007                 }
1008         }
1009         /* Put the final parameter of the FJOIN into a tokenstream ready to split it */
1010         irc::tokenstream users(params[2]);
1011         std::string item = "*";
1012         /* do this first, so our mode reversals are correctly received by other servers
1013          * if there is a TS collision.
1014          */
1015         params[2] = ":" + params[2];
1016         Utils->DoOneToAllButSender(source,"FJOIN",params,source);
1017         /* Now, process every 'prefixes,nick' pair */
1018         while (item != "")
1019         {
1020                 /* Find next user */
1021                 item = users.GetToken();
1022                 const char* usr = item.c_str();
1023                 /* Safety check just to make sure someones not sent us an FJOIN full of spaces
1024                  * (is this even possible?) */
1025                 if (usr && *usr)
1026                 {
1027                         const char* permissions = usr;
1028                         int ntimes = 0;
1029                         char* nm = new char[MAXBUF];
1030                         char* tnm = nm;
1031                         /* Iterate through all the prefix values, convert them from prefixes
1032                          * to mode letters, and append them to the mode sequence
1033                          */
1034                         while ((*permissions) && (*permissions != ',') && (ntimes < MAXBUF))
1035                         {
1036                                 ModeHandler* mh = Instance->Modes->FindPrefix(*permissions);
1037                                 if (mh)
1038                                 {
1039                                         /* This is a valid prefix */
1040                                         ntimes++;
1041                                         *tnm++ = mh->GetModeChar();
1042                                 }
1043                                 else
1044                                 {
1045                                         /* Not a valid prefix...
1046                                          * danger bill bobbertson! (that's will robinsons older brother ;-) ...)
1047                                          */
1048                                         this->Instance->WriteOpers("ERROR: We received a user with an unknown prefix '%c'. Closed connection to avoid a desync.",*permissions);
1049                                         this->WriteLine(std::string("ERROR :Invalid prefix '")+(*permissions)+"' in FJOIN");
1050                                         return false;
1051                                 }
1052                                 usr++;
1053                                 permissions++;
1054                         }
1055                         /* Null terminate modes */
1056                         *tnm = 0;
1057                         /* Advance past the comma, to the nick */
1058                         usr++;
1059                         /* Check the user actually exists */
1060                         who = this->Instance->FindNick(usr);
1061                         if (who)
1062                         {
1063                                 /* Did they get any modes? How many times? */
1064                                 strlcat(modestring, nm, MAXBUF);
1065                                 for (int k = 0; k < ntimes; k++)
1066                                         mode_users[modectr++] = strdup(usr);
1067                                 /* Free temporary buffer used for mode sequence */
1068                                 delete[] nm;
1069                                 /* Check that the user's 'direction' is correct
1070                                  * based on the server sending the FJOIN. We must
1071                                  * check each nickname in turn, because the origin of
1072                                  * the FJOIN may be different to the origin of the nicks
1073                                  * in the command itself.
1074                                  */
1075                                 TreeServer* route_back_again = Utils->BestRouteTo(who->server);
1076                                 if ((!route_back_again) || (route_back_again->GetSocket() != this))
1077                                 {
1078                                         /* Oh dear oh dear. */
1079                                         continue;
1080                                 }
1081                                 /* Finally, we can actually place the user into the channel.
1082                                  * We're sure its right. Final answer, phone a friend.
1083                                  */
1084                                 chanrec::JoinUser(this->Instance, who, channel.c_str(), true, "");
1085                                 /* Have we already queued up MAXMODES modes with parameters
1086                                  * (+qaohv) ready to be sent to the server?
1087                                  */
1088                                 if (modectr >= (MAXMODES-1))
1089                                 {
1090                                         /* Only actually give the users any status if we lost
1091                                          * the FJOIN or drew (equal timestamps).
1092                                          * It isn't actually possible for ourTS to be > TS here,
1093                                          * only possible to actually have ourTS == TS, or
1094                                          * ourTS < TS, because if we lost, we already lowered
1095                                          * our TS above before we entered this loop. We only
1096                                          * check >= as a safety measure, in case someone stuffed
1097                                          * up. If someone DID stuff up, it was most likely me.
1098                                          * Note: I do not like baseball bats in the face...
1099                                          */
1100                                         if (ourTS >= TS)
1101                                         {
1102                                                 this->Instance->SendMode((const char**)mode_users,modectr,who);
1103
1104                                                 /* Something stuffed up, and for some reason, the timestamp is
1105                                                  * NOT lowered right now and should be. Lower it. Usually this
1106                                                  * code won't be executed, doubtless someone will remove it some
1107                                                  * day soon.
1108                                                  */
1109                                                 if (ourTS > TS)
1110                                                 {
1111                                                         Instance->Log(DEFAULT,"Channel TS for %s changed from %lu to %lu",chan->name,ourTS,TS);
1112                                                         chan->age = TS;
1113                                                         ourTS = TS;
1114                                                 }
1115                                         }
1116
1117                                         /* Reset all this back to defaults, and
1118                                          * free any ram we have left allocated.
1119                                          */
1120                                         strcpy(mode_users[1],"+");
1121                                         for (unsigned int f = 2; f < modectr; f++)
1122                                                 free(mode_users[f]);
1123                                         modectr = 2;
1124                                 }
1125                         }
1126                         else
1127                         {
1128                                 /* Remember to free this */
1129                                 delete[] nm;
1130                                 /* If we got here, there's a nick in FJOIN which doesnt exist on this server.
1131                                  * We don't try to process the nickname here (that WOULD cause a segfault because
1132                                  * we'd be playing with null pointers) however, we DO pass the nickname on, just
1133                                  * in case somehow we're desynched, so that other users which might be able to see
1134                                  * the nickname get their fair chance to process it.
1135                                  */
1136                                 Instance->Log(SPARSE,"Warning! Invalid user in FJOIN to channel %s IGNORED", channel.c_str());
1137                                 continue;
1138                         }
1139                 }
1140         }
1141
1142         /* there werent enough modes built up to flush it during FJOIN,
1143          * or, there are a number left over. flush them out.
1144          */
1145         if ((modectr > 2) && (who) && (chan))
1146         {
1147                 if (ourTS >= TS)
1148                 {
1149                         /* Our channel is newer than theirs. Evil deeds must be afoot. */
1150                         this->Instance->SendMode((const char**)mode_users,modectr,who);
1151                         /* Yet again, we can't actually get a true value here, if everything else
1152                          * is working as it should.
1153                          */
1154                         if (ourTS > TS)
1155                         {
1156                                 Instance->Log(DEFAULT,"Channel TS for %s changed from %lu to %lu",chan->name,ourTS,TS);
1157                                 chan->age = TS;
1158                                 ourTS = TS;
1159                         }
1160                 }
1161
1162                 /* Free anything we have left to free */
1163                 for (unsigned int f = 2; f < modectr; f++)
1164                         free(mode_users[f]);
1165         }
1166         /* if we newly created the channel, set it's TS properly. */
1167         if (created)
1168         {
1169                 /* find created channel .. */
1170                 chan = this->Instance->FindChan(channel);
1171                 if (chan)
1172                         /* w00t said this shouldnt be needed but it is.
1173                          * This isnt strictly true, as chan can be NULL
1174                          * if a nick collision has occured and therefore
1175                          * the channel was never created.
1176                          */
1177                         chan->age = TS;
1178         }
1179         /* All done. That wasnt so bad was it, you can wipe
1180          * the sweat from your forehead now. :-)
1181          */
1182         return true;
1183 }
1184
1185 /** NICK command */
1186 bool TreeSocket::IntroduceClient(const std::string &source, std::deque<std::string> &params)
1187 {
1188         if (params.size() < 8)
1189                 return true;
1190         if (params.size() > 8)
1191         {
1192                 this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+params[1]+" :Invalid client introduction ("+params[1]+"?)");
1193                 return true;
1194         }
1195         // NICK age nick host dhost ident +modes ip :gecos
1196         //       0    1   2     3     4      5   6     7
1197         time_t age = atoi(params[0].c_str());
1198         
1199         const char* tempnick = params[1].c_str();
1200         Instance->Log(DEBUG,"New remote client %s",tempnick);
1201         
1202         user_hash::iterator iter = this->Instance->clientlist->find(tempnick);
1203                 
1204         if (iter != this->Instance->clientlist->end())
1205         {
1206                 // nick collision
1207                 this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" KILL "+tempnick+" :Nickname collision");
1208                 userrec::QuitUser(this->Instance, iter->second, "Nickname collision");
1209                 return true;
1210         }
1211
1212         userrec* _new = new userrec(this->Instance);
1213         (*(this->Instance->clientlist))[tempnick] = _new;
1214         _new->SetFd(FD_MAGIC_NUMBER);
1215         strlcpy(_new->nick, tempnick,NICKMAX-1);
1216         strlcpy(_new->host, params[2].c_str(),63);
1217         strlcpy(_new->dhost, params[3].c_str(),63);
1218         _new->server = this->Instance->FindServerNamePtr(source.c_str());
1219         strlcpy(_new->ident, params[4].c_str(),IDENTMAX);
1220         strlcpy(_new->fullname, params[7].c_str(),MAXGECOS);
1221         _new->registered = REG_ALL;
1222         _new->signon = age;
1223                 
1224         /*
1225          * we need to remove the + from the modestring, so we can do our stuff
1226          */
1227         std::string::size_type pos_after_plus = params[5].find_first_not_of('+');
1228         if (pos_after_plus != std::string::npos)
1229         params[5] = params[5].substr(pos_after_plus);
1230
1231         for (std::string::iterator v = params[5].begin(); v != params[5].end(); v++)
1232         {
1233                 _new->modes[(*v)-65] = 1;
1234                 /* For each mode thats set, increase counter */
1235                 ModeHandler* mh = Instance->Modes->FindMode(*v, MODETYPE_USER);
1236                 if (mh)
1237                         mh->ChangeCount(1);
1238         }
1239
1240         /* now we've done with modes processing, put the + back for remote servers */
1241         params[5] = "+" + params[5];
1242
1243 #ifdef SUPPORT_IP6LINKS
1244         if (params[6].find_first_of(":") != std::string::npos)
1245                 _new->SetSockAddr(AF_INET6, params[6].c_str(), 0);
1246         else
1247 #endif
1248                 _new->SetSockAddr(AF_INET, params[6].c_str(), 0);
1249
1250         Instance->AddGlobalClone(_new);
1251         this->Instance->SNO->WriteToSnoMask('C',"Client connecting at %s: %s!%s@%s [%s]",_new->server,_new->nick,_new->ident,_new->host, _new->GetIPString());
1252
1253         params[7] = ":" + params[7];
1254         Utils->DoOneToAllButSender(source,"NICK", params, source);
1255
1256         // Increment the Source Servers User Count..
1257         TreeServer* SourceServer = Utils->FindServer(source);
1258         if (SourceServer)
1259         {
1260                 SourceServer->AddUserCount();
1261         }
1262
1263         FOREACH_MOD_I(Instance,I_OnPostConnect,OnPostConnect(_new));
1264
1265         return true;
1266 }
1267
1268 /** Send one or more FJOINs for a channel of users.
1269  * If the length of a single line is more than 480-NICKMAX
1270  * in length, it is split over multiple lines.
1271  */
1272 void TreeSocket::SendFJoins(TreeServer* Current, chanrec* c)
1273 {
1274         std::string buffer;
1275         char list[MAXBUF];
1276         std::string individual_halfops = std::string(":")+this->Instance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age);
1277                 
1278         size_t dlen, curlen;
1279         dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age);
1280         int numusers = 0;
1281         char* ptr = list + dlen;
1282
1283         CUList *ulist = c->GetUsers();
1284         std::string modes = "";
1285         std::string params = "";
1286
1287         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1288         {
1289                 // The first parameter gets a : before it
1290                 size_t ptrlen = snprintf(ptr, MAXBUF, " %s%s,%s", !numusers ? ":" : "", c->GetAllPrefixChars(i->second), i->second->nick);
1291
1292                 curlen += ptrlen;
1293                 ptr += ptrlen;
1294
1295                 numusers++;
1296
1297                 if (curlen > (480-NICKMAX))
1298                 {
1299                         buffer.append(list).append("\r\n");
1300                         dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->ServerName,c->name,(unsigned long)c->age);
1301                         ptr = list + dlen;
1302                         ptrlen = 0;
1303                         numusers = 0;
1304                 }
1305         }
1306
1307         if (numusers)
1308                 buffer.append(list).append("\r\n");
1309
1310         /* Sorry for the hax. Because newly created channels assume +nt,
1311          * if this channel doesnt have +nt, explicitly send -n and -t for the missing modes.
1312          */
1313         bool inverted = false;
1314         if (!c->IsModeSet('n'))
1315         {
1316                 modes.append("-n");
1317                 inverted = true;
1318         }
1319         if (!c->IsModeSet('t'))
1320         {
1321                 modes.append("-t");
1322                 inverted = true;
1323         }
1324         if (inverted)
1325         {
1326                 modes.append("+");
1327         }
1328
1329         for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
1330         {
1331                 modes.append("b");
1332                 params.append(" ").append(b->data);
1333                 if (params.length() >= MAXMODES)
1334                 {
1335                         /* Wrap at MAXMODES */
1336                         buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
1337                         modes = "";
1338                         params = "";
1339                 }
1340         }
1341         buffer.append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(c->ChanModes(true));
1342
1343         /* Only send these if there are any */
1344         if (!modes.empty())
1345                 buffer.append("\r\n").append(":").append(this->Instance->Config->ServerName).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
1346
1347         this->WriteLine(buffer);
1348 }
1349
1350 /** Send G, Q, Z and E lines */
1351 void TreeSocket::SendXLines(TreeServer* Current)
1352 {
1353         char data[MAXBUF];
1354         std::string buffer;
1355         std::string n = this->Instance->Config->ServerName;
1356         const char* sn = n.c_str();
1357         /* Yes, these arent too nice looking, but they get the job done */
1358         for (std::vector<ZLine*>::iterator i = Instance->XLines->zlines.begin(); i != Instance->XLines->zlines.end(); i++)
1359         {
1360                 snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1361                 buffer.append(data);
1362         }
1363         for (std::vector<QLine*>::iterator i = Instance->XLines->qlines.begin(); i != Instance->XLines->qlines.end(); i++)
1364         {
1365                 snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1366                 buffer.append(data);
1367         }
1368         for (std::vector<GLine*>::iterator i = Instance->XLines->glines.begin(); i != Instance->XLines->glines.end(); i++)
1369         {
1370                 snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1371                 buffer.append(data);
1372         }
1373         for (std::vector<ELine*>::iterator i = Instance->XLines->elines.begin(); i != Instance->XLines->elines.end(); i++)
1374         {
1375                 snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1376                 buffer.append(data);
1377         }
1378         for (std::vector<ZLine*>::iterator i = Instance->XLines->pzlines.begin(); i != Instance->XLines->pzlines.end(); i++)
1379         {
1380                 snprintf(data,MAXBUF,":%s ADDLINE Z %s %s %lu %lu :%s\r\n",sn,(*i)->ipaddr,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1381                 buffer.append(data);
1382         }
1383         for (std::vector<QLine*>::iterator i = Instance->XLines->pqlines.begin(); i != Instance->XLines->pqlines.end(); i++)
1384         {
1385                 snprintf(data,MAXBUF,":%s ADDLINE Q %s %s %lu %lu :%s\r\n",sn,(*i)->nick,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1386                 buffer.append(data);
1387         }
1388         for (std::vector<GLine*>::iterator i = Instance->XLines->pglines.begin(); i != Instance->XLines->pglines.end(); i++)
1389         {
1390                 snprintf(data,MAXBUF,":%s ADDLINE G %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1391                 buffer.append(data);
1392         }
1393         for (std::vector<ELine*>::iterator i = Instance->XLines->pelines.begin(); i != Instance->XLines->pelines.end(); i++)
1394         {
1395                 snprintf(data,MAXBUF,":%s ADDLINE E %s@%s %s %lu %lu :%s\r\n",sn,(*i)->identmask,(*i)->hostmask,(*i)->source,(unsigned long)(*i)->set_time,(unsigned long)(*i)->duration,(*i)->reason);
1396                 buffer.append(data);
1397         }
1398
1399         if (!buffer.empty())
1400                 this->WriteLine(buffer);
1401 }
1402
1403 /** Send channel modes and topics */
1404 void TreeSocket::SendChannelModes(TreeServer* Current)
1405 {
1406         char data[MAXBUF];
1407         std::deque<std::string> list;
1408         std::string n = this->Instance->Config->ServerName;
1409         const char* sn = n.c_str();
1410         for (chan_hash::iterator c = this->Instance->chanlist->begin(); c != this->Instance->chanlist->end(); c++)
1411         {
1412                 SendFJoins(Current, c->second);
1413                 if (*c->second->topic)
1414                 {
1415                         snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",sn,c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic);
1416                         this->WriteLine(data);
1417                 }
1418                 FOREACH_MOD_I(this->Instance,I_OnSyncChannel,OnSyncChannel(c->second,(Module*)Utils->Creator,(void*)this));
1419                 list.clear();
1420                 c->second->GetExtList(list);
1421                 for (unsigned int j = 0; j < list.size(); j++)
1422                 {
1423                         FOREACH_MOD_I(this->Instance,I_OnSyncChannelMetaData,OnSyncChannelMetaData(c->second,(Module*)Utils->Creator,(void*)this,list[j]));
1424                 }
1425         }
1426 }
1427
1428 /** send all users and their oper state/modes */
1429 void TreeSocket::SendUsers(TreeServer* Current)
1430 {
1431         char data[MAXBUF];
1432         std::deque<std::string> list;
1433         std::string dataline;
1434         for (user_hash::iterator u = this->Instance->clientlist->begin(); u != this->Instance->clientlist->end(); u++)
1435         {
1436                 if (u->second->registered == REG_ALL)
1437                 {
1438                         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->FormatModes(),u->second->GetIPString(),u->second->fullname);
1439                         this->WriteLine(data);
1440                         if (*u->second->oper)
1441                         {
1442                                 snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->nick, u->second->oper);
1443                                 this->WriteLine(data);
1444                         }
1445                         if (*u->second->awaymsg)
1446                         {
1447                                 snprintf(data,MAXBUF,":%s AWAY :%s", u->second->nick, u->second->awaymsg);
1448                                 this->WriteLine(data);
1449                         }
1450                         FOREACH_MOD_I(this->Instance,I_OnSyncUser,OnSyncUser(u->second,(Module*)Utils->Creator,(void*)this));
1451                         list.clear();
1452                         u->second->GetExtList(list);
1453                         for (unsigned int j = 0; j < list.size(); j++)
1454                         {
1455                                 FOREACH_MOD_I(this->Instance,I_OnSyncUserMetaData,OnSyncUserMetaData(u->second,(Module*)Utils->Creator,(void*)this,list[j]));
1456                         }
1457                 }
1458         }
1459 }
1460
1461 /** This function is called when we want to send a netburst to a local
1462  * server. There is a set order we must do this, because for example
1463  * users require their servers to exist, and channels require their
1464  * users to exist. You get the idea.
1465  */
1466 void TreeSocket::DoBurst(TreeServer* s)
1467 {
1468         std::string burst = "BURST "+ConvToStr(Instance->Time(true));
1469         std::string endburst = "ENDBURST";
1470         // Because by the end of the netburst, it  could be gone!
1471         std::string name = s->GetName();
1472         this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2"+name+"\2.");
1473         this->WriteLine(burst);
1474         /* send our version string */
1475         this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" VERSION :"+this->Instance->GetVersionString());
1476         /* Send server tree */
1477         this->SendServers(Utils->TreeRoot,s,1);
1478         /* Send users and their oper status */
1479         this->SendUsers(s);
1480         /* Send everything else (channel modes, xlines etc) */
1481         this->SendChannelModes(s);
1482         this->SendXLines(s);            
1483         FOREACH_MOD_I(this->Instance,I_OnSyncOtherMetaData,OnSyncOtherMetaData((Module*)Utils->Creator,(void*)this));
1484         this->WriteLine(endburst);
1485         this->Instance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+name+"\2.");
1486 }
1487
1488 /** This function is called when we receive data from a remote
1489  * server. We buffer the data in a std::string (it doesnt stay
1490  * there for long), reading using InspSocket::Read() which can
1491  * read up to 16 kilobytes in one operation.
1492  *
1493  * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
1494  * THE SOCKET OBJECT FOR US.
1495  */
1496 bool TreeSocket::OnDataReady()
1497 {
1498         char* data = this->Read();
1499         /* Check that the data read is a valid pointer and it has some content */
1500         if (data && *data)
1501         {
1502                 this->in_buffer.append(data);
1503                 /* While there is at least one new line in the buffer,
1504                  * do something useful (we hope!) with it.
1505                  */
1506                 while (in_buffer.find("\n") != std::string::npos)
1507                 {
1508                         std::string ret = in_buffer.substr(0,in_buffer.find("\n")-1);
1509                         in_buffer = in_buffer.substr(in_buffer.find("\n")+1,in_buffer.length()-in_buffer.find("\n"));
1510                         /* Use rfind here not find, as theres more
1511                          * chance of the \r being near the end of the
1512                          * string, not the start.
1513                          */
1514                         if (ret.find("\r") != std::string::npos)
1515                                 ret = in_buffer.substr(0,in_buffer.find("\r")-1);
1516                         /* Process this one, abort if it
1517                          * didnt return true.
1518                          */
1519                         if (!this->ProcessLine(ret))
1520                         {
1521                                 return false;
1522                         }
1523                 }
1524                 return true;
1525         }
1526         /* EAGAIN returns an empty but non-NULL string, so this
1527          * evaluates to TRUE for EAGAIN but to FALSE for EOF.
1528          */
1529         return (data && !*data);
1530 }
1531
1532 int TreeSocket::WriteLine(std::string line)
1533 {
1534         Instance->Log(DEBUG, "-> %s", line.c_str());
1535         line.append("\r\n");
1536         return this->Write(line);
1537 }
1538
1539 /* Handle ERROR command */
1540 bool TreeSocket::Error(std::deque<std::string> &params)
1541 {
1542         if (params.size() < 1)
1543                 return false;
1544         this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(InboundServerName != "" ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str());
1545         /* we will return false to cause the socket to close. */
1546         return false;
1547 }
1548
1549 /** remote MOTD. leet, huh? */
1550 bool TreeSocket::Motd(const std::string &prefix, std::deque<std::string> &params)
1551 {
1552         if (params.size() > 0)
1553         {
1554                 if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
1555                 {
1556                         /* It's for our server */
1557                         string_list results;
1558                         userrec* source = this->Instance->FindNick(prefix);
1559
1560                         if (source)
1561                         {
1562                                 std::deque<std::string> par;
1563                                 par.push_back(prefix);
1564                                 par.push_back("");
1565
1566                                 if (!Instance->Config->MOTD.size())
1567                                 {
1568                                         par[1] = std::string("::")+Instance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing.";
1569                                         Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1570                                         return true;
1571                                 }
1572    
1573                                 par[1] = std::string("::")+Instance->Config->ServerName+" 375 "+source->nick+" :"+Instance->Config->ServerName+" message of the day";
1574                                 Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1575    
1576                                 for (unsigned int i = 0; i < Instance->Config->MOTD.size(); i++)
1577                                 {
1578                                         par[1] = std::string("::")+Instance->Config->ServerName+" 372 "+source->nick+" :- "+Instance->Config->MOTD[i];
1579                                         Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1580                                 }
1581      
1582                                 par[1] = std::string("::")+Instance->Config->ServerName+" 376 "+source->nick+" End of message of the day.";
1583                                 Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1584                         }
1585                 }
1586                 else
1587                 {
1588                         /* Pass it on */
1589                         userrec* source = this->Instance->FindNick(prefix);
1590                         if (source)
1591                                 Utils->DoOneToOne(prefix, "MOTD", params, params[0]);
1592                 }
1593         }
1594         return true;
1595 }
1596
1597 /** remote ADMIN. leet, huh? */
1598 bool TreeSocket::Admin(const std::string &prefix, std::deque<std::string> &params)
1599 {
1600         if (params.size() > 0)
1601         {
1602                 if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
1603                 {
1604                         /* It's for our server */
1605                         string_list results;
1606                         userrec* source = this->Instance->FindNick(prefix);
1607                         if (source)
1608                         {
1609                                 std::deque<std::string> par;
1610                                 par.push_back(prefix);
1611                                 par.push_back("");
1612                                 par[1] = std::string("::")+Instance->Config->ServerName+" 256 "+source->nick+" :Administrative info for "+Instance->Config->ServerName;
1613                                 Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1614                                 par[1] = std::string("::")+Instance->Config->ServerName+" 257 "+source->nick+" :Name     - "+Instance->Config->AdminName;
1615                                 Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1616                                 par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :Nickname - "+Instance->Config->AdminNick;
1617                                 Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1618                                 par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :E-Mail   - "+Instance->Config->AdminEmail;
1619                                 Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1620                         }
1621                 }
1622                 else
1623                 {
1624                         /* Pass it on */
1625                         userrec* source = this->Instance->FindNick(prefix);
1626                         if (source)
1627                                 Utils->DoOneToOne(prefix, "ADMIN", params, params[0]);
1628                 }
1629         }
1630         return true;
1631 }
1632
1633 bool TreeSocket::Stats(const std::string &prefix, std::deque<std::string> &params)
1634 {
1635         /* Get the reply to a STATS query if it matches this servername,
1636          * and send it back as a load of PUSH queries
1637          */
1638         if (params.size() > 1)
1639         {
1640                 if (this->Instance->MatchText(this->Instance->Config->ServerName, params[1]))
1641                 {
1642                         /* It's for our server */
1643                         string_list results;
1644                         userrec* source = this->Instance->FindNick(prefix);
1645                         if (source)
1646                         {
1647                                 std::deque<std::string> par;
1648                                 par.push_back(prefix);
1649                                 par.push_back("");
1650                                 DoStats(this->Instance, *(params[0].c_str()), source, results);
1651                                 for (size_t i = 0; i < results.size(); i++)
1652                                 {
1653                                         par[1] = "::" + results[i];
1654                                         Utils->DoOneToOne(this->Instance->Config->ServerName, "PUSH",par, source->server);
1655                                 }
1656                         }
1657                 }
1658                 else
1659                 {
1660                         /* Pass it on */
1661                         userrec* source = this->Instance->FindNick(prefix);
1662                         if (source)
1663                                 Utils->DoOneToOne(prefix, "STATS", params, params[1]);
1664                 }
1665         }
1666         return true;
1667 }
1668
1669
1670 /** Because the core won't let users or even SERVERS set +o,
1671  * we use the OPERTYPE command to do this.
1672  */
1673 bool TreeSocket::OperType(const std::string &prefix, std::deque<std::string> &params)
1674 {
1675         if (params.size() != 1)
1676                 return true;
1677         std::string opertype = params[0];
1678         userrec* u = this->Instance->FindNick(prefix);
1679         if (u)
1680         {
1681                 u->modes[UM_OPERATOR] = 1;
1682                 this->Instance->all_opers.push_back(u);
1683                 strlcpy(u->oper,opertype.c_str(),NICKMAX-1);
1684                 Utils->DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
1685                 this->Instance->SNO->WriteToSnoMask('o',"From %s: User %s (%s@%s) is now an IRC operator of type %s",u->server, u->nick,u->ident,u->host,irc::Spacify(opertype.c_str()));
1686         }
1687         return true;
1688 }
1689
1690 /** Because Andy insists that services-compatible servers must
1691  * implement SVSNICK and SVSJOIN, that's exactly what we do :p
1692  */
1693 bool TreeSocket::ForceNick(const std::string &prefix, std::deque<std::string> &params)
1694 {
1695         if (params.size() < 3)
1696                 return true;
1697
1698         userrec* u = this->Instance->FindNick(params[0]);
1699
1700         if (u)
1701         {
1702                 Utils->DoOneToAllButSender(prefix,"SVSNICK",params,prefix);
1703                 if (IS_LOCAL(u))
1704                 {
1705                         std::deque<std::string> par;
1706                         par.push_back(params[1]);
1707                         if (!u->ForceNickChange(params[1].c_str()))
1708                         {
1709                                 userrec::QuitUser(this->Instance, u, "Nickname collision");
1710                                 return true;
1711                         }
1712                         u->age = atoi(params[2].c_str());
1713                 }
1714         }
1715         return true;
1716 }
1717
1718 /*
1719  * Remote SQUIT (RSQUIT). Routing works similar to SVSNICK: Route it to the server that the target is connected to locally,
1720  * then let that server do the dirty work (squit it!). Example:
1721  * A -> B -> C -> D: oper on A squits D, A routes to B, B routes to C, C notices D connected locally, kills it. -- w00t
1722  */
1723 bool TreeSocket::RemoteSquit(const std::string &prefix, std::deque<std::string> &params)
1724 {
1725         /* ok.. :w00t RSQUIT jupe.barafranca.com :reason here */
1726         if (params.size() < 2)
1727                 return true;
1728
1729         TreeServer* s = Utils->FindServerMask(params[0]);
1730
1731         if (s)
1732         {
1733                 if (s == Utils->TreeRoot)
1734                 {
1735                         this->Instance->SNO->WriteToSnoMask('l',"What the fuck, I recieved a remote SQUIT for myself? :< (from %s", prefix.c_str());
1736                         return true;
1737                 }
1738
1739                 TreeSocket* sock = s->GetSocket();
1740
1741                 if (sock)
1742                 {
1743                         /* it's locally connected, KILL IT! */
1744                         Instance->SNO->WriteToSnoMask('l',"RSQUIT: Server \002%s\002 removed from network by %s: %s", params[0].c_str(), prefix.c_str(), params[1].c_str());
1745                         sock->Squit(s,"Server quit by " + prefix + ": " + params[1]);
1746                         Instance->SE->DelFd(sock);
1747                         sock->Close();
1748                         delete sock;
1749                 }
1750                 else
1751                 {
1752                         /* route the rsquit */
1753                         params[1] = ":" + params[1];
1754                         Utils->DoOneToOne(prefix, "RSQUIT", params, params[0]);
1755                 }
1756         }
1757         else
1758         {
1759                 /* mother fucker! it doesn't exist */
1760         }
1761
1762         return true;
1763 }
1764
1765 bool TreeSocket::ServiceJoin(const std::string &prefix, std::deque<std::string> &params)
1766 {
1767         if (params.size() < 2)
1768                 return true;
1769
1770         userrec* u = this->Instance->FindNick(params[0]);
1771
1772         if (u)
1773         {
1774                 /* only join if it's local, otherwise just pass it on! */
1775                 if (IS_LOCAL(u))
1776                         chanrec::JoinUser(this->Instance, u, params[1].c_str(), false);
1777                 Utils->DoOneToAllButSender(prefix,"SVSJOIN",params,prefix);
1778         }
1779         return true;
1780 }
1781
1782 bool TreeSocket::RemoteRehash(const std::string &prefix, std::deque<std::string> &params)
1783 {
1784         if (params.size() < 1)
1785                 return false;
1786
1787         std::string servermask = params[0];
1788
1789         if (this->Instance->MatchText(this->Instance->Config->ServerName,servermask))
1790         {
1791                 this->Instance->SNO->WriteToSnoMask('l',"Remote rehash initiated by \002"+prefix+"\002.");
1792                 this->Instance->RehashServer();
1793                 Utils->ReadConfiguration(false);
1794                 InitializeDisabledCommands(Instance->Config->DisabledCommands, Instance);
1795         }
1796         Utils->DoOneToAllButSender(prefix,"REHASH",params,prefix);
1797         return true;
1798 }
1799
1800 bool TreeSocket::RemoteKill(const std::string &prefix, std::deque<std::string> &params)
1801 {
1802         if (params.size() != 2)
1803                 return true;
1804
1805         std::string nick = params[0];
1806         userrec* u = this->Instance->FindNick(prefix);
1807         userrec* who = this->Instance->FindNick(nick);
1808
1809         if (who)
1810         {
1811                 /* Prepend kill source, if we don't have one */
1812                 std::string sourceserv = prefix;
1813                 if (u)
1814                 {
1815                         sourceserv = u->server;
1816                 }
1817                 if (*(params[1].c_str()) != '[')
1818                 {
1819                         params[1] = "[" + sourceserv + "] Killed (" + params[1] +")";
1820                 }
1821                 std::string reason = params[1];
1822                 params[1] = ":" + params[1];
1823                 Utils->DoOneToAllButSender(prefix,"KILL",params,sourceserv);
1824                 who->Write(":%s KILL %s :%s (%s)", sourceserv.c_str(), who->nick, sourceserv.c_str(), reason.c_str());
1825                 userrec::QuitUser(this->Instance,who,reason);
1826         }
1827         return true;
1828 }
1829
1830 bool TreeSocket::LocalPong(const std::string &prefix, std::deque<std::string> &params)
1831 {
1832         if (params.size() < 1)
1833                 return true;
1834
1835         if (params.size() == 1)
1836         {
1837                 TreeServer* ServerSource = Utils->FindServer(prefix);
1838                 if (ServerSource)
1839                 {
1840                         ServerSource->SetPingFlag();
1841                 }
1842         }
1843         else
1844         {
1845                 std::string forwardto = params[1];
1846                 if (forwardto == this->Instance->Config->ServerName)
1847                 {
1848                         /*
1849                          * this is a PONG for us
1850                          * if the prefix is a user, check theyre local, and if they are,
1851                          * dump the PONG reply back to their fd. If its a server, do nowt.
1852                          * Services might want to send these s->s, but we dont need to yet.
1853                          */
1854                         userrec* u = this->Instance->FindNick(prefix);
1855                         if (u)
1856                         {
1857                                 u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str());
1858                         }
1859                 }
1860                 else
1861                 {
1862                         // not for us, pass it on :)
1863                         Utils->DoOneToOne(prefix,"PONG",params,forwardto);
1864                 }
1865         }
1866
1867         return true;
1868 }
1869         
1870 bool TreeSocket::MetaData(const std::string &prefix, std::deque<std::string> &params)
1871 {
1872         if (params.size() < 3)
1873                 return true;
1874         TreeServer* ServerSource = Utils->FindServer(prefix);
1875         if (ServerSource)
1876         {
1877                 if (params[0] == "*")
1878                 {
1879                         FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_OTHER,NULL,params[1],params[2]));
1880                 }
1881                 else if (*(params[0].c_str()) == '#')
1882                 {
1883                         chanrec* c = this->Instance->FindChan(params[0]);
1884                         if (c)
1885                         {
1886                                 FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2]));
1887                         }
1888                 }
1889                 else if (*(params[0].c_str()) != '#')
1890                 {
1891                         userrec* u = this->Instance->FindNick(params[0]);
1892                         if (u)
1893                         {
1894                                 FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_USER,u,params[1],params[2]));
1895                         }
1896                 }
1897         }
1898
1899         params[2] = ":" + params[2];
1900         Utils->DoOneToAllButSender(prefix,"METADATA",params,prefix);
1901         return true;
1902 }
1903
1904 bool TreeSocket::ServerVersion(const std::string &prefix, std::deque<std::string> &params)
1905 {
1906         if (params.size() < 1)
1907                 return true;
1908
1909         TreeServer* ServerSource = Utils->FindServer(prefix);
1910
1911         if (ServerSource)
1912         {
1913                 ServerSource->SetVersion(params[0]);
1914         }
1915         params[0] = ":" + params[0];
1916         Utils->DoOneToAllButSender(prefix,"VERSION",params,prefix);
1917         return true;
1918 }
1919
1920 bool TreeSocket::ChangeHost(const std::string &prefix, std::deque<std::string> &params)
1921 {
1922         if (params.size() < 1)
1923                 return true;
1924         userrec* u = this->Instance->FindNick(prefix);
1925
1926         if (u)
1927         {
1928                 u->ChangeDisplayedHost(params[0].c_str());
1929                 Utils->DoOneToAllButSender(prefix,"FHOST",params,u->server);
1930         }
1931         return true;
1932 }
1933
1934 bool TreeSocket::AddLine(const std::string &prefix, std::deque<std::string> &params)
1935 {
1936         if (params.size() < 6)
1937                 return true;
1938         bool propogate = false;
1939         if (!this->bursting)
1940                 Utils->lines_to_apply = 0;
1941         switch (*(params[0].c_str()))
1942         {
1943                 case 'Z':
1944                         propogate = Instance->XLines->add_zline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
1945                         Instance->XLines->zline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
1946                         if (propogate)
1947                                 Utils->lines_to_apply |= APPLY_ZLINES;
1948                 break;
1949                 case 'Q':
1950                         propogate = Instance->XLines->add_qline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
1951                         Instance->XLines->qline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
1952                         if (propogate)
1953                                 Utils->lines_to_apply |= APPLY_QLINES;
1954                 break;
1955                 case 'E':
1956                         propogate = Instance->XLines->add_eline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
1957                         Instance->XLines->eline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
1958                 break;
1959                 case 'G':
1960                         propogate = Instance->XLines->add_gline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
1961                         Instance->XLines->gline_set_creation_time(params[1].c_str(), atoi(params[3].c_str()));
1962                         if (propogate)
1963                                 Utils->lines_to_apply |= APPLY_GLINES;
1964                 break;
1965                 case 'K':
1966                         propogate = Instance->XLines->add_kline(atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
1967                         if (propogate)
1968                                 Utils->lines_to_apply |= APPLY_KLINES;
1969                 break;
1970                 default:
1971                         /* Just in case... */
1972                         this->Instance->SNO->WriteToSnoMask('x',"\2WARNING\2: Invalid xline type '"+params[0]+"' sent by server "+prefix+", ignored!");
1973                         propogate = false;
1974                 break;
1975         }
1976         /* Send it on its way */
1977         if (propogate)
1978         {
1979                 if (atoi(params[4].c_str()))
1980                 {
1981                         this->Instance->SNO->WriteToSnoMask('x',"%s Added %cLINE on %s to expire in %lu seconds (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),atoi(params[4].c_str()),params[5].c_str());
1982                 }
1983                 else
1984                 {
1985                         this->Instance->SNO->WriteToSnoMask('x',"%s Added permenant %cLINE on %s (%s).",prefix.c_str(),*(params[0].c_str()),params[1].c_str(),params[5].c_str());
1986                 }
1987                 params[5] = ":" + params[5];
1988                 Utils->DoOneToAllButSender(prefix,"ADDLINE",params,prefix);
1989         }
1990         if (!this->bursting)
1991         {
1992                 Instance->XLines->apply_lines(Utils->lines_to_apply);
1993                 Utils->lines_to_apply = 0;
1994         }
1995         return true;
1996 }
1997
1998 bool TreeSocket::ChangeName(const std::string &prefix, std::deque<std::string> &params)
1999 {
2000         if (params.size() < 1)
2001                 return true;
2002         userrec* u = this->Instance->FindNick(prefix);
2003         if (u)
2004         {
2005                 u->ChangeName(params[0].c_str());
2006                 params[0] = ":" + params[0];
2007                 Utils->DoOneToAllButSender(prefix,"FNAME",params,u->server);
2008         }
2009         return true;
2010 }
2011
2012 bool TreeSocket::Whois(const std::string &prefix, std::deque<std::string> &params)
2013 {
2014         if (params.size() < 1)
2015                 return true;
2016         userrec* u = this->Instance->FindNick(prefix);
2017         if (u)
2018         {
2019                 // an incoming request
2020                 if (params.size() == 1)
2021                 {
2022                         userrec* x = this->Instance->FindNick(params[0]);
2023                         if ((x) && (IS_LOCAL(x)))
2024                         {
2025                                 userrec* x = this->Instance->FindNick(params[0]);
2026                                 char signon[MAXBUF];
2027                                 char idle[MAXBUF];
2028                                 snprintf(signon,MAXBUF,"%lu",(unsigned long)x->signon);
2029                                 snprintf(idle,MAXBUF,"%lu",(unsigned long)abs((x->idle_lastmsg)-Instance->Time(true)));
2030                                 std::deque<std::string> par;
2031                                 par.push_back(prefix);
2032                                 par.push_back(signon);
2033                                 par.push_back(idle);
2034                                 // ours, we're done, pass it BACK
2035                                 Utils->DoOneToOne(params[0],"IDLE",par,u->server);
2036                         }
2037                         else
2038                         {
2039                                 // not ours pass it on
2040                                 Utils->DoOneToOne(prefix,"IDLE",params,x->server);
2041                         }
2042                 }
2043                 else if (params.size() == 3)
2044                 {
2045                         std::string who_did_the_whois = params[0];
2046                         userrec* who_to_send_to = this->Instance->FindNick(who_did_the_whois);
2047                         if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
2048                         {
2049                                 // an incoming reply to a whois we sent out
2050                                 std::string nick_whoised = prefix;
2051                                 unsigned long signon = atoi(params[1].c_str());
2052                                 unsigned long idle = atoi(params[2].c_str());
2053                                 if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
2054                                         do_whois(this->Instance,who_to_send_to,u,signon,idle,nick_whoised.c_str());
2055                         }
2056                         else
2057                         {
2058                                 // not ours, pass it on
2059                                 Utils->DoOneToOne(prefix,"IDLE",params,who_to_send_to->server);
2060                         }
2061                 }
2062         }
2063         return true;
2064 }
2065
2066 bool TreeSocket::Push(const std::string &prefix, std::deque<std::string> &params)
2067 {
2068         if (params.size() < 2)
2069                 return true;
2070         userrec* u = this->Instance->FindNick(params[0]);
2071         if (!u)
2072                 return true;
2073         if (IS_LOCAL(u))
2074         {
2075                 u->Write(params[1]);
2076         }
2077         else
2078         {
2079                 // continue the raw onwards
2080                 params[1] = ":" + params[1];
2081                 Utils->DoOneToOne(prefix,"PUSH",params,u->server);
2082         }
2083         return true;
2084 }
2085
2086 bool TreeSocket::HandleSetTime(const std::string &prefix, std::deque<std::string> &params)
2087 {
2088         if (!params.size() || !Utils->EnableTimeSync)
2089                 return true;
2090         
2091         bool force = false;
2092         
2093         if ((params.size() == 2) && (params[1] == "FORCE"))
2094                 force = true;
2095         
2096         time_t rts = atoi(params[0].c_str());
2097         time_t us = Instance->Time(true);
2098         
2099         if (rts == us)
2100         {                       
2101                 Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix);
2102         }
2103         else if (force || (rts < us))
2104         {
2105                 int old = Instance->SetTimeDelta(rts - us);
2106                 Instance->Log(DEBUG, "%s TS (diff %d) from %s applied (old delta was %d)", (force) ? "Forced" : "Lower", rts - us, prefix.c_str(), old);
2107                 
2108                 Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix);
2109         }
2110         else
2111         {
2112                 Instance->Log(DEBUG, "Higher TS (diff %d) from %s overridden", us - rts, prefix.c_str());
2113                 
2114                 std::deque<std::string> oparams;
2115                 oparams.push_back(ConvToStr(us));
2116                 
2117                 Utils->DoOneToMany(prefix, "TIMESET", oparams);
2118         }
2119         
2120         return true;
2121 }
2122
2123 bool TreeSocket::Time(const std::string &prefix, std::deque<std::string> &params)
2124 {
2125         // :source.server TIME remote.server sendernick
2126         // :remote.server TIME source.server sendernick TS
2127         if (params.size() == 2)
2128         {
2129                 // someone querying our time?
2130                 if (this->Instance->Config->ServerName == params[0])
2131                 {
2132                         userrec* u = this->Instance->FindNick(params[1]);
2133                         if (u)
2134                         {
2135                                 params.push_back(ConvToStr(Instance->Time(false)));
2136                                 params[0] = prefix;
2137                                 Utils->DoOneToOne(this->Instance->Config->ServerName,"TIME",params,params[0]);
2138                         }
2139                 }
2140                 else
2141                 {
2142                         // not us, pass it on
2143                         userrec* u = this->Instance->FindNick(params[1]);
2144                         if (u)
2145                                 Utils->DoOneToOne(prefix,"TIME",params,params[0]);
2146                 }
2147         }
2148         else if (params.size() == 3)
2149         {
2150                 // a response to a previous TIME
2151                 userrec* u = this->Instance->FindNick(params[1]);
2152                 if ((u) && (IS_LOCAL(u)))
2153                 {
2154                         time_t rawtime = atol(params[2].c_str());
2155                         struct tm * timeinfo;
2156                         timeinfo = localtime(&rawtime);
2157                         char tms[26];
2158                         snprintf(tms,26,"%s",asctime(timeinfo));
2159                         tms[24] = 0;
2160                         u->WriteServ("391 %s %s :%s",u->nick,prefix.c_str(),tms);
2161                 }
2162                 else
2163                 {
2164                         if (u)
2165                                 Utils->DoOneToOne(prefix,"TIME",params,u->server);
2166                 }
2167         }
2168         return true;
2169 }
2170         
2171 bool TreeSocket::LocalPing(const std::string &prefix, std::deque<std::string> &params)
2172 {
2173         if (params.size() < 1)
2174                 return true;
2175         if (params.size() == 1)
2176         {
2177                 std::string stufftobounce = params[0];
2178                 this->WriteLine(std::string(":")+this->Instance->Config->ServerName+" PONG "+stufftobounce);
2179                 return true;
2180         }
2181         else
2182         {
2183                 std::string forwardto = params[1];
2184                 if (forwardto == this->Instance->Config->ServerName)
2185                 {
2186                         // this is a ping for us, send back PONG to the requesting server
2187                         params[1] = params[0];
2188                         params[0] = forwardto;
2189                         Utils->DoOneToOne(forwardto,"PONG",params,params[1]);
2190                 }
2191                 else
2192                 {
2193                         // not for us, pass it on :)
2194                         Utils->DoOneToOne(prefix,"PING",params,forwardto);
2195                 }
2196                 return true;
2197         }
2198 }
2199
2200 bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque<std::string> &params)
2201 {
2202         if (params.size() < 1)
2203                 return true;
2204         chanrec* c = Instance->FindChan(params[0]);
2205         if (c)
2206         {
2207                 irc::modestacker modestack(false);
2208                 CUList *ulist = c->GetUsers();
2209                 const char* y[127];
2210                 std::deque<std::string> stackresult;
2211                 std::string x;
2212                 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
2213                 {
2214                         std::string modesequence = Instance->Modes->ModeString(i->second, c);
2215                         if (modesequence.length())
2216                         {
2217                                 irc::spacesepstream sep(modesequence);
2218                                 std::string modeletters = sep.GetToken();
2219                                 while (!modeletters.empty())
2220                                 {
2221                                         char mletter = *(modeletters.begin());
2222                                         modestack.Push(mletter,sep.GetToken());
2223                                         modeletters.erase(modeletters.begin());
2224                                 }
2225                         }
2226                 }
2227
2228                 while (modestack.GetStackedLine(stackresult))
2229                 {
2230                         stackresult.push_front(ConvToStr(c->age));
2231                         stackresult.push_front(c->name);
2232                         Utils->DoOneToMany(Instance->Config->ServerName, "FMODE", stackresult);
2233                         stackresult.erase(stackresult.begin() + 1);
2234                         for (size_t z = 0; z < stackresult.size(); z++)
2235                         {
2236                                 y[z] = stackresult[z].c_str();
2237                         }
2238                         userrec* n = new userrec(Instance);
2239                         n->SetFd(FD_MAGIC_NUMBER);
2240                         Instance->SendMode(y, stackresult.size(), n);
2241                         delete n;
2242                 }
2243         }
2244         return true;
2245 }
2246
2247 bool TreeSocket::RemoteServer(const std::string &prefix, std::deque<std::string> &params)
2248 {
2249         if (params.size() < 4)
2250                 return false;
2251         std::string servername = params[0];
2252         std::string password = params[1];
2253         // hopcount is not used for a remote server, we calculate this ourselves
2254         std::string description = params[3];
2255         TreeServer* ParentOfThis = Utils->FindServer(prefix);
2256         if (!ParentOfThis)
2257         {
2258                 this->WriteLine("ERROR :Protocol error - Introduced remote server from unknown server "+prefix);
2259                 return false;
2260         }
2261         TreeServer* CheckDupe = Utils->FindServer(servername);
2262         if (CheckDupe)
2263         {
2264                 this->WriteLine("ERROR :Server "+servername+" already exists!");
2265                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+servername+"\2 denied, already exists");
2266                 return false;
2267         }
2268         TreeServer* Node = new TreeServer(this->Utils,this->Instance,servername,description,ParentOfThis,NULL);
2269         ParentOfThis->AddChild(Node);
2270         params[3] = ":" + params[3];
2271         Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
2272         this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")");
2273         return true;
2274 }
2275
2276 bool TreeSocket::Outbound_Reply_Server(std::deque<std::string> &params)
2277 {
2278         if (params.size() < 4)
2279                 return false;
2280
2281         irc::string servername = params[0].c_str();
2282         std::string sname = params[0];
2283         std::string password = params[1];
2284         int hops = atoi(params[2].c_str());
2285
2286         if (hops)
2287         {
2288                 this->WriteLine("ERROR :Server too far away for authentication");
2289                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
2290                 return false;
2291         }
2292         std::string description = params[3];
2293         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
2294         {
2295                 if ((x->Name == servername) && (x->RecvPass == password))
2296                 {
2297                         TreeServer* CheckDupe = Utils->FindServer(sname);
2298                         if (CheckDupe)
2299                         {
2300                                 this->WriteLine("ERROR :Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
2301                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
2302                                 return false;
2303                         }
2304                         // Begin the sync here. this kickstarts the
2305                         // other side, waiting in WAIT_AUTH_2 state,
2306                         // into starting their burst, as it shows
2307                         // that we're happy.
2308                         this->LinkState = CONNECTED;
2309                         // we should add the details of this server now
2310                         // to the servers tree, as a child of the root
2311                         // node.
2312                         TreeServer* Node = new TreeServer(this->Utils,this->Instance,sname,description,Utils->TreeRoot,this);
2313                         Utils->TreeRoot->AddChild(Node);
2314                         params[3] = ":" + params[3];
2315                         Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,sname);
2316                         this->bursting = true;
2317                         this->DoBurst(Node);
2318                         return true;
2319                 }
2320         }
2321         this->WriteLine("ERROR :Invalid credentials");
2322         this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
2323         return false;
2324 }
2325
2326 bool TreeSocket::Inbound_Server(std::deque<std::string> &params)
2327 {
2328         if (params.size() < 4)
2329                 return false;
2330         irc::string servername = params[0].c_str();
2331         std::string sname = params[0];
2332         std::string password = params[1];
2333         int hops = atoi(params[2].c_str());
2334
2335         if (hops)
2336         {
2337                 this->WriteLine("ERROR :Server too far away for authentication");
2338                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
2339                 return false;
2340         }
2341         std::string description = params[3];
2342         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
2343         {
2344                 if ((x->Name == servername) && (x->RecvPass == password))
2345                 {
2346                         TreeServer* CheckDupe = Utils->FindServer(sname);
2347                         if (CheckDupe)
2348                         {
2349                                 this->WriteLine("ERROR :Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
2350                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
2351                                 return false;
2352                         }
2353                         this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] ("+description+")");
2354                         if (this->Hook)
2355                         {
2356                                 std::string name = InspSocketNameRequest((Module*)Utils->Creator, this->Hook).Send();
2357                                 this->Instance->SNO->WriteToSnoMask('l',"Connection from \2"+sname+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+name+"\2");
2358                         }
2359
2360                         this->InboundServerName = sname;
2361                         this->InboundDescription = description;
2362                         // this is good. Send our details: Our server name and description and hopcount of 0,
2363                         // along with the sendpass from this block.
2364                         this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+x->SendPass+" 0 :"+this->Instance->Config->ServerDesc);
2365                         // move to the next state, we are now waiting for THEM.
2366                         this->LinkState = WAIT_AUTH_2;
2367                         return true;
2368                 }
2369         }
2370         this->WriteLine("ERROR :Invalid credentials");
2371         this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
2372         return false;
2373 }
2374
2375 void TreeSocket::Split(const std::string &line, std::deque<std::string> &n)
2376 {
2377         n.clear();
2378         irc::tokenstream tokens(line);
2379         std::string param;
2380         while ((param = tokens.GetToken()) != "")
2381                 n.push_back(param);
2382         return;
2383 }
2384
2385 bool TreeSocket::ProcessLine(std::string &line)
2386 {
2387         std::deque<std::string> params;
2388         irc::string command;
2389         std::string prefix;
2390                 
2391         line = line.substr(0, line.find_first_of("\r\n"));
2392                 
2393         if (line.empty())
2394                 return true;
2395                 
2396         Instance->Log(DEBUG, "<- %s", line.c_str());
2397                 
2398         this->Split(line.c_str(),params);
2399                 
2400         if ((params[0][0] == ':') && (params.size() > 1))
2401         {
2402                 prefix = params[0].substr(1);
2403                 params.pop_front();
2404         }
2405         command = params[0].c_str();
2406         params.pop_front();
2407         switch (this->LinkState)
2408         {
2409                 TreeServer* Node;
2410                 
2411                 case WAIT_AUTH_1:
2412                         // Waiting for SERVER command from remote server. Server initiating
2413                         // the connection sends the first SERVER command, listening server
2414                         // replies with theirs if its happy, then if the initiator is happy,
2415                         // it starts to send its net sync, which starts the merge, otherwise
2416                         // it sends an ERROR.
2417                         if (command == "PASS")
2418                         {
2419                                 /* Silently ignored */
2420                         }
2421                         else if (command == "SERVER")
2422                         {
2423                                 return this->Inbound_Server(params);
2424                         }
2425                         else if (command == "ERROR")
2426                         {
2427                                 return this->Error(params);
2428                         }
2429                         else if (command == "USER")
2430                         {
2431                                 this->WriteLine("ERROR :Client connections to this port are prohibited.");
2432                                 return false;
2433                         }
2434                         else if (command == "CAPAB")
2435                         {
2436                                 return this->Capab(params);
2437                         }
2438                         else if ((command == "U") || (command == "S"))
2439                         {
2440                                 this->WriteLine("ERROR :Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
2441                                 return false;
2442                         }
2443                         else
2444                         {
2445                                 std::string error("ERROR :Invalid command in negotiation phase: ");
2446                                 error.append(command.c_str());
2447                                 this->WriteLine(error);
2448                                 return false;
2449                         }
2450                 break;
2451                 case WAIT_AUTH_2:
2452                         // Waiting for start of other side's netmerge to say they liked our
2453                         // password.
2454                         if (command == "SERVER")
2455                         {
2456                                 // cant do this, they sent it to us in the WAIT_AUTH_1 state!
2457                                 // silently ignore.
2458                                 return true;
2459                         }
2460                         else if ((command == "U") || (command == "S"))
2461                         {
2462                                 this->WriteLine("ERROR :Cannot use the old-style mesh linking protocol with m_spanningtree.so!");
2463                                 return false;
2464                         }
2465                         else if (command == "BURST")
2466                         {
2467                                 if (params.size() && Utils->EnableTimeSync)
2468                                 {
2469                                         /* If a time stamp is provided, apply synchronization */
2470                                         bool force = false;
2471                                         time_t them = atoi(params[0].c_str());
2472                                         time_t us = Instance->Time(true);
2473                                         int delta = them - us;
2474                                         if ((params.size() == 2) && (params[1] == "FORCE"))
2475                                                 force = true;
2476                                         if ((delta < -600) || (delta > 600))
2477                                         {
2478                                                 this->Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than ten minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta));
2479                                                 this->WriteLine("ERROR :Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than ten minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
2480                                                 return false;
2481                                         }
2482                                         
2483                                         if (force || (us > them))
2484                                         {
2485                                                 this->Instance->SetTimeDelta(them - us);
2486                                                 // Send this new timestamp to any other servers
2487                                                 Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
2488                                         }
2489                                         else
2490                                         {
2491                                                 // Override the timestamp
2492                                                 this->WriteLine(":" + Utils->TreeRoot->GetName() + " TIMESET " + ConvToStr(us));
2493                                         }
2494                                 }
2495                                 this->LinkState = CONNECTED;
2496                                 Node = new TreeServer(this->Utils,this->Instance,InboundServerName,InboundDescription,Utils->TreeRoot,this);
2497                                 Utils->TreeRoot->AddChild(Node);
2498                                 params.clear();
2499                                 params.push_back(InboundServerName);
2500                                 params.push_back("*");
2501                                 params.push_back("1");
2502                                 params.push_back(":"+InboundDescription);
2503                                 Utils->DoOneToAllButSender(Utils->TreeRoot->GetName(),"SERVER",params,InboundServerName);
2504                                 this->bursting = true;
2505                                 this->DoBurst(Node);
2506                         }
2507                         else if (command == "ERROR")
2508                         {
2509                                 return this->Error(params);
2510                         }
2511                         else if (command == "CAPAB")
2512                         {
2513                                 return this->Capab(params);
2514                         }
2515                         
2516                 break;
2517                 case LISTENER:
2518                         this->WriteLine("ERROR :Internal error -- listening socket accepted its own descriptor!!!");
2519                         return false;
2520                 break;
2521                 case CONNECTING:
2522                         if (command == "SERVER")
2523                         {
2524                                 // another server we connected to, which was in WAIT_AUTH_1 state,
2525                                 // has just sent us their credentials. If we get this far, theyre
2526                                 // happy with OUR credentials, and they are now in WAIT_AUTH_2 state.
2527                                 // if we're happy with this, we should send our netburst which
2528                                 // kickstarts the merge.
2529                                 return this->Outbound_Reply_Server(params);
2530                         }
2531                         else if (command == "ERROR")
2532                         {
2533                                 return this->Error(params);
2534                         }
2535                 break;
2536                 case CONNECTED:
2537                         // This is the 'authenticated' state, when all passwords
2538                         // have been exchanged and anything past this point is taken
2539                         // as gospel.
2540                         
2541                         if (prefix != "")
2542                         {
2543                                 std::string direction = prefix;
2544                                 userrec* t = this->Instance->FindNick(prefix);
2545                                 if (t)
2546                                 {
2547                                         direction = t->server;
2548                                 }
2549                                 TreeServer* route_back_again = Utils->BestRouteTo(direction);
2550                                 if ((!route_back_again) || (route_back_again->GetSocket() != this))
2551                                 {
2552                                         if (route_back_again)
2553                                                 Instance->Log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str());
2554                                         return true;
2555                                 }
2556                                 /* Fix by brain:
2557                                  * When there is activity on the socket, reset the ping counter so
2558                                  * that we're not wasting bandwidth pinging an active server.
2559                                  */ 
2560                                 route_back_again->SetNextPingTime(time(NULL) + 60);
2561                                 route_back_again->SetPingFlag();
2562                         }
2563                         
2564                         if (command == "SVSMODE")
2565                         {
2566                                 /* Services expects us to implement
2567                                  * SVSMODE. In inspircd its the same as
2568                                  * MODE anyway.
2569                                  */
2570                                 command = "MODE";
2571                         }
2572                         std::string target = "";
2573                         /* Yes, know, this is a mess. Its reasonably fast though as we're
2574                          * working with std::string here.
2575                          */
2576                         if ((command == "NICK") && (params.size() > 1))
2577                         {
2578                                 return this->IntroduceClient(prefix,params);
2579                         }
2580                         else if (command == "FJOIN")
2581                         {
2582                                 return this->ForceJoin(prefix,params);
2583                         }
2584                         else if (command == "STATS")
2585                         {
2586                                 return this->Stats(prefix, params);
2587                         }
2588                         else if (command == "MOTD")
2589                         {
2590                                 return this->Motd(prefix, params);
2591                         }
2592                         else if (command == "ADMIN")
2593                         {
2594                                 return this->Admin(prefix, params);
2595                         }
2596                         else if (command == "SERVER")
2597                         {
2598                                 return this->RemoteServer(prefix,params);
2599                         }
2600                         else if (command == "ERROR")
2601                         {
2602                                 return this->Error(params);
2603                         }
2604                         else if (command == "OPERTYPE")
2605                         {
2606                                 return this->OperType(prefix,params);
2607                         }
2608                         else if (command == "FMODE")
2609                         {
2610                                 return this->ForceMode(prefix,params);
2611                         }
2612                         else if (command == "KILL")
2613                         {
2614                                 return this->RemoteKill(prefix,params);
2615                         }
2616                         else if (command == "FTOPIC")
2617                         {
2618                                 return this->ForceTopic(prefix,params);
2619                         }
2620                         else if (command == "REHASH")
2621                         {
2622                                 return this->RemoteRehash(prefix,params);
2623                         }
2624                         else if (command == "METADATA")
2625                         {
2626                                 return this->MetaData(prefix,params);
2627                         }
2628                         else if (command == "REMSTATUS")
2629                         {
2630                                 return this->RemoveStatus(prefix,params);
2631                         }
2632                         else if (command == "PING")
2633                         {
2634                                 /*
2635                                  * We just got a ping from a server that's bursting.
2636                                  * This can't be right, so set them to not bursting, and
2637                                  * apply their lines.
2638                                  */
2639                                 if (this->bursting)
2640                                 {
2641                                         this->bursting = false;
2642                                         Instance->XLines->apply_lines(Utils->lines_to_apply);
2643                                         Utils->lines_to_apply = 0;
2644                                 }
2645                                 if (prefix == "")
2646                                 {
2647                                         prefix = this->GetName();
2648                                 }
2649                                 return this->LocalPing(prefix,params);
2650                         }
2651                         else if (command == "PONG")
2652                         {
2653                                 /*
2654                                  * We just got a pong from a server that's bursting.
2655                                  * This can't be right, so set them to not bursting, and
2656                                  * apply their lines.
2657                                  */
2658                                 if (this->bursting)
2659                                 {
2660                                         this->bursting = false;
2661                                         Instance->XLines->apply_lines(Utils->lines_to_apply);
2662                                         Utils->lines_to_apply = 0;
2663                                 }
2664                                 if (prefix == "")
2665                                 {
2666                                         prefix = this->GetName();
2667                                 }
2668                                 return this->LocalPong(prefix,params);
2669                         }
2670                         else if (command == "VERSION")
2671                         {
2672                                 return this->ServerVersion(prefix,params);
2673                         }
2674                         else if (command == "FHOST")
2675                         {
2676                                 return this->ChangeHost(prefix,params);
2677                         }
2678                         else if (command == "FNAME")
2679                         {
2680                                 return this->ChangeName(prefix,params);
2681                         }
2682                         else if (command == "ADDLINE")
2683                         {
2684                                 return this->AddLine(prefix,params);
2685                         }
2686                         else if (command == "SVSNICK")
2687                         {
2688                                 if (prefix == "")
2689                                 {
2690                                         prefix = this->GetName();
2691                                 }
2692                                 return this->ForceNick(prefix,params);
2693                         }
2694                         else if (command == "RSQUIT")
2695                         {
2696                                 return this->RemoteSquit(prefix, params);
2697                         }
2698                         else if (command == "IDLE")
2699                         {
2700                                 return this->Whois(prefix,params);
2701                         }
2702                         else if (command == "PUSH")
2703                         {
2704                                 return this->Push(prefix,params);
2705                         }
2706                         else if (command == "TIMESET")
2707                         {
2708                                 return this->HandleSetTime(prefix, params);
2709                         }
2710                         else if (command == "TIME")
2711                         {
2712                                 return this->Time(prefix,params);
2713                         }
2714                         else if ((command == "KICK") && (Utils->IsServer(prefix)))
2715                         {
2716                                 std::string sourceserv = this->myhost;
2717                                 if (params.size() == 3)
2718                                 {
2719                                         userrec* user = this->Instance->FindNick(params[1]);
2720                                         chanrec* chan = this->Instance->FindChan(params[0]);
2721                                         if (user && chan)
2722                                         {
2723                                                 if (!chan->ServerKickUser(user, params[2].c_str(), false))
2724                                                         /* Yikes, the channels gone! */
2725                                                         delete chan;
2726                                         }
2727                                 }
2728                                 if (this->InboundServerName != "")
2729                                 {
2730                                         sourceserv = this->InboundServerName;
2731                                 }
2732                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
2733                         }
2734                         else if (command == "SVSJOIN")
2735                         {
2736                                 if (prefix == "")
2737                                 {
2738                                         prefix = this->GetName();
2739                                 }
2740                                 return this->ServiceJoin(prefix,params);
2741                         }
2742                         else if (command == "SQUIT")
2743                         {
2744                                 if (params.size() == 2)
2745                                 {
2746                                         this->Squit(Utils->FindServer(params[0]),params[1]);
2747                                 }
2748                                 return true;
2749                         }
2750                         else if (command == "OPERNOTICE")
2751                         {
2752                                 std::string sourceserv = this->myhost;
2753                                 if (this->InboundServerName != "")
2754                                         sourceserv = this->InboundServerName;
2755                                 if (params.size() >= 1)
2756                                         Instance->WriteOpers("*** From " + sourceserv + ": " + params[0]);
2757                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
2758                         }
2759                         else if (command == "MODENOTICE")
2760                         {
2761                                 std::string sourceserv = this->myhost;
2762                                 if (this->InboundServerName != "")
2763                                         sourceserv = this->InboundServerName;
2764                                 if (params.size() >= 2)
2765                                 {
2766                                         Instance->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", sourceserv.c_str(), params[1].c_str());
2767                                 }
2768                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
2769                         }
2770                         else if (command == "SNONOTICE")
2771                         {
2772                                 std::string sourceserv = this->myhost;
2773                                 if (this->InboundServerName != "")
2774                                         sourceserv = this->InboundServerName;
2775                                 if (params.size() >= 2)
2776                                 {
2777                                         Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + sourceserv + ": "+ params[1]);
2778                                 }
2779                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
2780                         }
2781                         else if (command == "ENDBURST")
2782                         {
2783                                 this->bursting = false;
2784                                 Instance->XLines->apply_lines(Utils->lines_to_apply);
2785                                 Utils->lines_to_apply = 0;
2786                                 std::string sourceserv = this->myhost;
2787                                 if (this->InboundServerName != "")
2788                                 {
2789                                         sourceserv = this->InboundServerName;
2790                                 }
2791                                 this->Instance->SNO->WriteToSnoMask('l',"Received end of netburst from \2%s\2",sourceserv.c_str());
2792
2793                                 Event rmode((char*)sourceserv.c_str(), (Module*)Utils->Creator, "new_server");
2794                                 rmode.Send(Instance);
2795
2796                                 return true;
2797                         }
2798                         else
2799                         {
2800                                 // not a special inter-server command.
2801                                 // Emulate the actual user doing the command,
2802                                 // this saves us having a huge ugly parser.
2803                                 userrec* who = this->Instance->FindNick(prefix);
2804                                 std::string sourceserv = this->myhost;
2805                                 if (this->InboundServerName != "")
2806                                 {
2807                                         sourceserv = this->InboundServerName;
2808                                 }
2809                                 if ((!who) && (command == "MODE"))
2810                                 {
2811                                         if (Utils->IsServer(prefix))
2812                                         {
2813                                                 const char* modelist[127];
2814                                                 for (size_t i = 0; i < params.size(); i++)
2815                                                         modelist[i] = params[i].c_str();
2816                                                 userrec* fake = new userrec(Instance);
2817                                                 fake->SetFd(FD_MAGIC_NUMBER);
2818                                                 this->Instance->SendMode(modelist, params.size(), fake);
2819
2820                                                 delete fake;
2821                                                 /* Hot potato! pass it on! */
2822                                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
2823                                         }
2824                                 }
2825                                 if (who)
2826                                 {
2827                                         if ((command == "NICK") && (params.size() > 0))
2828                                         {
2829                                                 /* On nick messages, check that the nick doesnt
2830                                                  * already exist here. If it does, kill their copy,
2831                                                  * and our copy.
2832                                                  */
2833                                                 userrec* x = this->Instance->FindNick(params[0]);
2834                                                 if ((x) && (x != who))
2835                                                 {
2836                                                         std::deque<std::string> p;
2837                                                         p.push_back(params[0]);
2838                                                         p.push_back("Nickname collision ("+prefix+" -> "+params[0]+")");
2839                                                         Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p);
2840                                                         p.clear();
2841                                                         p.push_back(prefix);
2842                                                         p.push_back("Nickname collision");
2843                                                         Utils->DoOneToMany(this->Instance->Config->ServerName,"KILL",p);
2844                                                         userrec::QuitUser(this->Instance,x,"Nickname collision ("+prefix+" -> "+params[0]+")");
2845                                                         userrec* y = this->Instance->FindNick(prefix);
2846                                                         if (y)
2847                                                         {
2848                                                                 userrec::QuitUser(this->Instance,y,"Nickname collision");
2849                                                         }
2850                                                         return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
2851                                                 }
2852                                         }
2853                                         // its a user
2854                                         target = who->server;
2855                                         const char* strparams[127];
2856                                         for (unsigned int q = 0; q < params.size(); q++)
2857                                         {
2858                                                 strparams[q] = params[q].c_str();
2859                                         }
2860                                         switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who))
2861                                         {
2862                                                 case CMD_INVALID:
2863                                                         this->WriteLine("ERROR :Unrecognised command '"+std::string(command.c_str())+"' -- possibly loaded mismatched modules");
2864                                                         return false;
2865                                                 break;
2866                                                 case CMD_FAILURE:
2867                                                         return true;
2868                                                 break;
2869                                                 default:
2870                                                         /* CMD_SUCCESS and CMD_USER_DELETED fall through here */
2871                                                 break;
2872                                         }
2873                                 }
2874                                 else
2875                                 {
2876                                         // its not a user. Its either a server, or somethings screwed up.
2877                                         if (Utils->IsServer(prefix))
2878                                                 target = this->Instance->Config->ServerName;
2879                                         else
2880                                                 return true;
2881                                 }
2882                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
2883
2884                         }
2885                         return true;
2886                 break;
2887         }
2888         return true;
2889 }
2890
2891 std::string TreeSocket::GetName()
2892 {
2893         std::string sourceserv = this->myhost;
2894         if (this->InboundServerName != "")
2895         {
2896                 sourceserv = this->InboundServerName;
2897         }
2898         return sourceserv;
2899 }
2900
2901 void TreeSocket::OnTimeout()
2902 {
2903         if (this->LinkState == CONNECTING)
2904         {
2905                 this->Instance->SNO->WriteToSnoMask('l',"CONNECT: Connection to \002"+myhost+"\002 timed out.");
2906                 Link* MyLink = Utils->FindLink(myhost);
2907                 if (MyLink)
2908                         Utils->DoFailOver(MyLink);
2909         }
2910 }
2911
2912 void TreeSocket::OnClose()
2913 {
2914         // Connection closed.
2915         // If the connection is fully up (state CONNECTED)
2916         // then propogate a netsplit to all peers.
2917         std::string quitserver = this->myhost;
2918         if (this->InboundServerName != "")
2919         {
2920                 quitserver = this->InboundServerName;
2921         }
2922         TreeServer* s = Utils->FindServer(quitserver);
2923         if (s)
2924         {
2925                 Squit(s,"Remote host closed the connection");
2926         }
2927
2928         if (quitserver != "")
2929                 this->Instance->SNO->WriteToSnoMask('l',"Connection to '\2%s\2' failed.",quitserver.c_str());
2930 }
2931
2932 int TreeSocket::OnIncomingConnection(int newsock, char* ip)
2933 {
2934         /* To prevent anyone from attempting to flood opers/DDoS by connecting to the server port,
2935          * or discovering if this port is the server port, we don't allow connections from any
2936          * IPs for which we don't have a link block.
2937          */
2938         bool found = false;
2939
2940         found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), ip) != Utils->ValidIPs.end());
2941         if (!found)
2942         {
2943                 for (vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++)
2944                         if (irc::sockets::MatchCIDR(ip, (*i).c_str()))
2945                                 found = true;
2946
2947                 if (!found)
2948                 {
2949                         this->Instance->SNO->WriteToSnoMask('l',"Server connection from %s denied (no link blocks with that IP address)", ip);
2950                         close(newsock);
2951                         return false;
2952                 }
2953         }
2954
2955         TreeSocket* s = new TreeSocket(this->Utils, this->Instance, newsock, ip, this->Hook);
2956         s = s; /* Whinge whinge whinge, thats all GCC ever does. */
2957         return true;
2958 }
2959
2960 /** This class is used to resolve server hostnames during /connect and autoconnect.
2961  * As of 1.1, the resolver system is seperated out from InspSocket, so we must do this
2962  * resolver step first ourselves if we need it. This is totally nonblocking, and will
2963  * callback to OnLookupComplete or OnError when completed. Once it has completed we
2964  * will have an IP address which we can then use to continue our connection.
2965  */
2966 class ServernameResolver : public Resolver
2967 {       
2968  private:
2969         /** A copy of the Link tag info for what we're connecting to.
2970          * We take a copy, rather than using a pointer, just in case the
2971          * admin takes the tag away and rehashes while the domain is resolving.
2972          */
2973         Link MyLink;
2974         SpanningTreeUtilities* Utils;
2975  public: 
2976         ServernameResolver(Module* me, SpanningTreeUtilities* Util, InspIRCd* Instance, const std::string &hostname, Link x, bool &cached) : Resolver(Instance, hostname, DNS_QUERY_FORWARD, cached, me), MyLink(x), Utils(Util)
2977         {
2978                 /* Nothing in here, folks */
2979         }
2980
2981         void OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
2982         {
2983                 /* Initiate the connection, now that we have an IP to use.
2984                  * Passing a hostname directly to InspSocket causes it to
2985                  * just bail and set its FD to -1.
2986                  */
2987                 TreeServer* CheckDupe = Utils->FindServer(MyLink.Name.c_str());
2988                 if (!CheckDupe) /* Check that nobody tried to connect it successfully while we were resolving */
2989                 {
2990
2991                         if ((!MyLink.Hook.empty()) && (Utils->hooks.find(MyLink.Hook.c_str()) ==  Utils->hooks.end()))
2992                                 return;
2993
2994                         TreeSocket* newsocket = new TreeSocket(this->Utils, ServerInstance, result,MyLink.Port,false,MyLink.Timeout ? MyLink.Timeout : 10,MyLink.Name.c_str(),
2995                                         MyLink.Hook.empty() ? NULL : Utils->hooks[MyLink.Hook.c_str()]);
2996                         if (newsocket->GetFd() > -1)
2997                         {
2998                                 /* We're all OK */
2999                         }
3000                         else
3001                         {
3002                                 /* Something barfed, show the opers */
3003                                 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",MyLink.Name.c_str(),strerror(errno));
3004                                 delete newsocket;
3005                                 Utils->DoFailOver(&MyLink);
3006                         }
3007                 }
3008         }
3009
3010         void OnError(ResolverError e, const std::string &errormessage)
3011         {
3012                 /* Ooops! */
3013                 ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: Unable to resolve hostname - %s",MyLink.Name.c_str(),errormessage.c_str());
3014                 Utils->DoFailOver(&MyLink);
3015         }
3016 };
3017
3018 /** Create a timer which recurs every second, we inherit from InspTimer.
3019  * InspTimer is only one-shot however, so at the end of each Tick() we simply
3020  * insert another of ourselves into the pending queue :)
3021  */
3022 class TimeSyncTimer : public InspTimer
3023 {
3024  private:
3025         InspIRCd *Instance;
3026         ModuleSpanningTree *Module;
3027  public:
3028         TimeSyncTimer(InspIRCd *Instance, ModuleSpanningTree *Mod);
3029         virtual void Tick(time_t TIME);
3030 };
3031
3032 HandshakeTimer::HandshakeTimer(InspIRCd* Inst, TreeSocket* s, Link* l, SpanningTreeUtilities* u) : InspTimer(1, time(NULL)), Instance(Inst), sock(s), lnk(l), Utils(u)
3033 {
3034         thefd = sock->GetFd();
3035 }
3036
3037 void HandshakeTimer::Tick(time_t TIME)
3038 {
3039         if (Instance->SE->GetRef(thefd) == sock)
3040         {
3041                 if (sock->GetHook() && InspSocketHSCompleteRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send())
3042                 {
3043                         InspSocketAttachCertRequest(sock, (Module*)Utils->Creator, sock->GetHook()).Send();
3044                         sock->SendCapabilities();
3045                         if (sock->GetLinkState() == CONNECTING)
3046                         {
3047                                 sock->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+lnk->SendPass+" 0 :"+this->Instance->Config->ServerDesc);
3048                         }
3049                 }
3050                 else
3051                 {
3052                         Instance->Timers->AddTimer(new HandshakeTimer(Instance, sock, lnk, Utils));
3053                 }
3054         }
3055 }
3056
3057 ModuleSpanningTree::ModuleSpanningTree(InspIRCd* Me)
3058         : Module::Module(Me), max_local(0), max_global(0)
3059 {
3060         ServerInstance->UseInterface("InspSocketHook");
3061         Utils = new SpanningTreeUtilities(Me, this);
3062         command_rconnect = new cmd_rconnect(ServerInstance, this, Utils);
3063         ServerInstance->AddCommand(command_rconnect);
3064         if (Utils->EnableTimeSync)
3065         {
3066                 SyncTimer = new TimeSyncTimer(ServerInstance, this);
3067                 ServerInstance->Timers->AddTimer(SyncTimer);
3068         }
3069         else
3070                 SyncTimer = NULL;
3071 }
3072
3073 void ModuleSpanningTree::ShowLinks(TreeServer* Current, userrec* user, int hops)
3074 {
3075         std::string Parent = Utils->TreeRoot->GetName();
3076         if (Current->GetParent())
3077         {
3078                 Parent = Current->GetParent()->GetName();
3079         }
3080         for (unsigned int q = 0; q < Current->ChildCount(); q++)
3081         {
3082                 if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))
3083                 {
3084                         if (*user->oper)
3085                         {
3086                                  ShowLinks(Current->GetChild(q),user,hops+1);
3087                         }
3088                 }
3089                 else
3090                 {
3091                         ShowLinks(Current->GetChild(q),user,hops+1);
3092                 }
3093         }
3094         /* Don't display the line if its a uline, hide ulines is on, and the user isnt an oper */
3095         if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetName().c_str())) && (!*user->oper))
3096                 return;
3097         user->WriteServ("364 %s %s %s :%d %s",user->nick,Current->GetName().c_str(),(Utils->FlatLinks && (!*user->oper)) ? ServerInstance->Config->ServerName : Parent.c_str(),(Utils->FlatLinks && (!*user->oper)) ? 0 : hops,Current->GetDesc().c_str());
3098 }
3099
3100 int ModuleSpanningTree::CountLocalServs()
3101 {
3102         return Utils->TreeRoot->ChildCount();
3103 }
3104
3105 int ModuleSpanningTree::CountServs()
3106 {
3107         return Utils->serverlist.size();
3108 }
3109
3110 void ModuleSpanningTree::HandleLinks(const char** parameters, int pcnt, userrec* user)
3111 {
3112         ShowLinks(Utils->TreeRoot,user,0);
3113         user->WriteServ("365 %s * :End of /LINKS list.",user->nick);
3114         return;
3115 }
3116
3117 void ModuleSpanningTree::HandleLusers(const char** parameters, int pcnt, userrec* user)
3118 {
3119         unsigned int n_users = ServerInstance->UserCount();
3120
3121         /* Only update these when someone wants to see them, more efficient */
3122         if ((unsigned int)ServerInstance->LocalUserCount() > max_local)
3123                 max_local = ServerInstance->LocalUserCount();
3124         if (n_users > max_global)
3125                 max_global = n_users;
3126
3127         unsigned int ulined_count = 0;
3128         unsigned int ulined_local_count = 0;
3129
3130         /* If ulined are hidden and we're not an oper, count the number of ulined servers hidden,
3131          * locally and globally (locally means directly connected to us)
3132          */
3133         if ((Utils->HideULines) && (!*user->oper))
3134         {
3135                 for (server_hash::iterator q = Utils->serverlist.begin(); q != Utils->serverlist.end(); q++)
3136                 {
3137                         if (ServerInstance->ULine(q->second->GetName().c_str()))
3138                         {
3139                                 ulined_count++;
3140                                 if (q->second->GetParent() == Utils->TreeRoot)
3141                                         ulined_local_count++;
3142                         }
3143                 }
3144         }
3145         user->WriteServ("251 %s :There are %d users and %d invisible on %d servers",user->nick,n_users-ServerInstance->InvisibleUserCount(),ServerInstance->InvisibleUserCount(),ulined_count ? this->CountServs() - ulined_count : this->CountServs());
3146         if (ServerInstance->OperCount())
3147                 user->WriteServ("252 %s %d :operator(s) online",user->nick,ServerInstance->OperCount());
3148         if (ServerInstance->UnregisteredUserCount())
3149                 user->WriteServ("253 %s %d :unknown connections",user->nick,ServerInstance->UnregisteredUserCount());
3150         if (ServerInstance->ChannelCount())
3151                 user->WriteServ("254 %s %d :channels formed",user->nick,ServerInstance->ChannelCount());
3152         user->WriteServ("254 %s :I have %d clients and %d servers",user->nick,ServerInstance->LocalUserCount(),ulined_local_count ? this->CountLocalServs() - ulined_local_count : this->CountLocalServs());
3153         user->WriteServ("265 %s :Current Local Users: %d  Max: %d",user->nick,ServerInstance->LocalUserCount(),max_local);
3154         user->WriteServ("266 %s :Current Global Users: %d  Max: %d",user->nick,n_users,max_global);
3155         return;
3156 }
3157
3158 // WARNING: NOT THREAD SAFE - DONT GET ANY SMART IDEAS.
3159 void ModuleSpanningTree::ShowMap(TreeServer* Current, userrec* user, int depth, char matrix[128][80], float &totusers, float &totservers)
3160 {
3161         if (line < 128)
3162         {
3163                 for (int t = 0; t < depth; t++)
3164                 {
3165                         matrix[line][t] = ' ';
3166                 }
3167                 // For Aligning, we need to work out exactly how deep this thing is, and produce
3168                 // a 'Spacer' String to compensate.
3169                 char spacer[40];
3170                 memset(spacer,' ',40);
3171                 if ((40 - Current->GetName().length() - depth) > 1) {
3172                         spacer[40 - Current->GetName().length() - depth] = '\0';
3173                 }
3174                 else
3175                 {
3176                         spacer[5] = '\0';
3177                 }
3178                 float percent;
3179                 char text[80];
3180                 if (ServerInstance->clientlist->size() == 0) {
3181                         // If there are no users, WHO THE HELL DID THE /MAP?!?!?!
3182                         percent = 0;
3183                 }
3184                 else
3185                 {
3186                         percent = ((float)Current->GetUserCount() / (float)ServerInstance->clientlist->size()) * 100;
3187                 }
3188                 snprintf(text, 80, "%s %s%5d [%5.2f%%]", Current->GetName().c_str(), spacer, Current->GetUserCount(), percent);
3189                 totusers += Current->GetUserCount();
3190                 totservers++;
3191                 strlcpy(&matrix[line][depth],text,80);
3192                 line++;
3193                 for (unsigned int q = 0; q < Current->ChildCount(); q++)
3194                 {
3195                         if ((Utils->HideULines) && (ServerInstance->ULine(Current->GetChild(q)->GetName().c_str())))
3196                         {
3197                                 if (*user->oper)
3198                                 {
3199                                         ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
3200                                 }
3201                         }
3202                         else
3203                         {
3204                                 ShowMap(Current->GetChild(q),user,(Utils->FlatLinks && (!*user->oper)) ? depth : depth+2,matrix,totusers,totservers);
3205                         }
3206                 }
3207         }
3208 }
3209
3210 int ModuleSpanningTree::HandleMotd(const char** parameters, int pcnt, userrec* user)
3211 {
3212         if (pcnt > 0)
3213         {
3214                 /* Remote MOTD, the server is within the 1st parameter */
3215                 std::deque<std::string> params;
3216                 params.push_back(parameters[0]);
3217                 /* Send it out remotely, generate no reply yet */
3218                 TreeServer* s = Utils->FindServerMask(parameters[0]);
3219                 if (s)
3220                 {
3221                         Utils->DoOneToOne(user->nick, "MOTD", params, s->GetName());
3222                 }
3223                 else
3224                 {
3225                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
3226                 }
3227                 return 1;
3228         }
3229         return 0;
3230 }
3231
3232 int ModuleSpanningTree::HandleAdmin(const char** parameters, int pcnt, userrec* user)
3233 {
3234         if (pcnt > 0)
3235         {
3236                 /* Remote ADMIN, the server is within the 1st parameter */
3237                 std::deque<std::string> params;
3238                 params.push_back(parameters[0]);
3239                 /* Send it out remotely, generate no reply yet */
3240                 TreeServer* s = Utils->FindServerMask(parameters[0]);
3241                 if (s)
3242                 {
3243                         Utils->DoOneToOne(user->nick, "ADMIN", params, s->GetName());
3244                 }
3245                 else
3246                 {
3247                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
3248                 }
3249                 return 1;
3250         }
3251         return 0;
3252 }
3253
3254 int ModuleSpanningTree::HandleStats(const char** parameters, int pcnt, userrec* user)
3255 {
3256         if (pcnt > 1)
3257         {
3258                 /* Remote STATS, the server is within the 2nd parameter */
3259                 std::deque<std::string> params;
3260                 params.push_back(parameters[0]);
3261                 params.push_back(parameters[1]);
3262                 /* Send it out remotely, generate no reply yet */
3263                 TreeServer* s = Utils->FindServerMask(parameters[1]);
3264                 if (s)
3265                 {
3266                         params[1] = s->GetName();
3267                         Utils->DoOneToOne(user->nick, "STATS", params, s->GetName());
3268                 }
3269                 else
3270                 {
3271                         user->WriteServ( "402 %s %s :No such server", user->nick, parameters[0]);
3272                 }
3273                 return 1;
3274         }
3275         return 0;
3276 }
3277
3278 // Ok, prepare to be confused.
3279 // After much mulling over how to approach this, it struck me that
3280 // the 'usual' way of doing a /MAP isnt the best way. Instead of
3281 // keeping track of a ton of ascii characters, and line by line
3282 // under recursion working out where to place them using multiplications
3283 // and divisons, we instead render the map onto a backplane of characters
3284 // (a character matrix), then draw the branches as a series of "L" shapes
3285 // from the nodes. This is not only friendlier on CPU it uses less stack.
3286 void ModuleSpanningTree::HandleMap(const char** parameters, int pcnt, userrec* user)
3287 {
3288         // This array represents a virtual screen which we will
3289         // "scratch" draw to, as the console device of an irc
3290         // client does not provide for a proper terminal.
3291         float totusers = 0;
3292         float totservers = 0;
3293         char matrix[128][80];
3294         for (unsigned int t = 0; t < 128; t++)
3295         {
3296                 matrix[t][0] = '\0';
3297         }
3298         line = 0;
3299         // The only recursive bit is called here.
3300         ShowMap(Utils->TreeRoot,user,0,matrix,totusers,totservers);
3301         // Process each line one by one. The algorithm has a limit of
3302         // 128 servers (which is far more than a spanning tree should have
3303         // anyway, so we're ok). This limit can be raised simply by making
3304         // the character matrix deeper, 128 rows taking 10k of memory.
3305         for (int l = 1; l < line; l++)
3306         {
3307                 // scan across the line looking for the start of the
3308                 // servername (the recursive part of the algorithm has placed
3309                 // the servers at indented positions depending on what they
3310                 // are related to)
3311                 int first_nonspace = 0;
3312                 while (matrix[l][first_nonspace] == ' ')
3313                 {
3314                         first_nonspace++;
3315                 }
3316                 first_nonspace--;
3317                 // Draw the `- (corner) section: this may be overwritten by
3318                 // another L shape passing along the same vertical pane, becoming
3319                 // a |- (branch) section instead.
3320                 matrix[l][first_nonspace] = '-';
3321                 matrix[l][first_nonspace-1] = '`';
3322                 int l2 = l - 1;
3323                 // Draw upwards until we hit the parent server, causing possibly
3324                 // other corners (`-) to become branches (|-)
3325                 while ((matrix[l2][first_nonspace-1] == ' ') || (matrix[l2][first_nonspace-1] == '`'))
3326                 {
3327                         matrix[l2][first_nonspace-1] = '|';
3328                         l2--;
3329                 }
3330         }
3331         // dump the whole lot to the user. This is the easy bit, honest.
3332         for (int t = 0; t < line; t++)
3333         {
3334                 user->WriteServ("006 %s :%s",user->nick,&matrix[t][0]);
3335         }
3336         float avg_users = totusers / totservers;
3337         user->WriteServ("270 %s :%.0f server%s and %.0f user%s, average %.2f users per server",user->nick,totservers,(totservers > 1 ? "s" : ""),totusers,(totusers > 1 ? "s" : ""),avg_users);
3338         user->WriteServ("007 %s :End of /MAP",user->nick);
3339         return;
3340 }
3341
3342 int ModuleSpanningTree::HandleSquit(const char** parameters, int pcnt, userrec* user)
3343 {
3344         TreeServer* s = Utils->FindServerMask(parameters[0]);
3345         if (s)
3346         {
3347                 if (s == Utils->TreeRoot)
3348                 {
3349                         user->WriteServ("NOTICE %s :*** SQUIT: Foolish mortal, you cannot make a server SQUIT itself! (%s matches local server name)",user->nick,parameters[0]);
3350                         return 1;
3351                 }
3352                 TreeSocket* sock = s->GetSocket();
3353                 if (sock)
3354                 {
3355                         ServerInstance->SNO->WriteToSnoMask('l',"SQUIT: Server \002%s\002 removed from network by %s",parameters[0],user->nick);
3356                         sock->Squit(s,std::string("Server quit by ") + user->GetFullRealHost());
3357                         ServerInstance->SE->DelFd(sock);
3358                         sock->Close();
3359                         delete sock;
3360                 }
3361                 else
3362                 {
3363                         /* route it */
3364                         std::deque<std::string> params;
3365                         params.push_back(parameters[0]);
3366                         params.push_back(std::string(":Server quit by ") + user->GetFullRealHost());
3367                         Utils->DoOneToOne(user->nick, "RSQUIT", params, parameters[0]);
3368                 }
3369         }
3370         else
3371         {
3372                  user->WriteServ("NOTICE %s :*** SQUIT: The server \002%s\002 does not exist on the network.",user->nick,parameters[0]);
3373         }
3374         return 1;
3375 }
3376
3377 int ModuleSpanningTree::HandleTime(const char** parameters, int pcnt, userrec* user)
3378 {
3379         if ((IS_LOCAL(user)) && (pcnt))
3380         {
3381                 TreeServer* found = Utils->FindServerMask(parameters[0]);
3382                 if (found)
3383                 {
3384                         // we dont' override for local server
3385                         if (found == Utils->TreeRoot)
3386                                 return 0;
3387                         
3388                         std::deque<std::string> params;
3389                         params.push_back(found->GetName());
3390                         params.push_back(user->nick);
3391                         Utils->DoOneToOne(ServerInstance->Config->ServerName,"TIME",params,found->GetName());
3392                 }
3393                 else
3394                 {
3395                         user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
3396                 }
3397         }
3398         return 1;
3399 }
3400
3401 int ModuleSpanningTree::HandleRemoteWhois(const char** parameters, int pcnt, userrec* user)
3402 {
3403         if ((IS_LOCAL(user)) && (pcnt > 1))
3404         {
3405                 userrec* remote = ServerInstance->FindNick(parameters[1]);
3406                 if ((remote) && (remote->GetFd() < 0))
3407                 {
3408                         std::deque<std::string> params;
3409                         params.push_back(parameters[1]);
3410                         Utils->DoOneToOne(user->nick,"IDLE",params,remote->server);
3411                         return 1;
3412                 }
3413                 else if (!remote)
3414                 {
3415                         user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[1]);
3416                         user->WriteServ("318 %s %s :End of /WHOIS list.",user->nick, parameters[1]);
3417                         return 1;
3418                 }
3419         }
3420         return 0;
3421 }
3422
3423 void ModuleSpanningTree::DoPingChecks(time_t curtime)
3424 {
3425         for (unsigned int j = 0; j < Utils->TreeRoot->ChildCount(); j++)
3426         {
3427                 TreeServer* serv = Utils->TreeRoot->GetChild(j);
3428                 TreeSocket* sock = serv->GetSocket();
3429                 if (sock)
3430                 {
3431                         if (curtime >= serv->NextPingTime())
3432                         {
3433                                 if (serv->AnsweredLastPing())
3434                                 {
3435                                         sock->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" PING "+serv->GetName());
3436                                         serv->SetNextPingTime(curtime + 60);
3437                                 }
3438                                 else
3439                                 {
3440                                         // they didnt answer, boot them
3441                                         ServerInstance->SNO->WriteToSnoMask('l',"Server \002%s\002 pinged out",serv->GetName().c_str());
3442                                         sock->Squit(serv,"Ping timeout");
3443                                         ServerInstance->SE->DelFd(sock);
3444                                         sock->Close();
3445                                         delete sock;
3446                                         return;
3447                                 }
3448                         }
3449                 }
3450         }
3451 }
3452
3453 void ModuleSpanningTree::ConnectServer(Link* x)
3454 {
3455         insp_inaddr binip;
3456         /* Do we already have an IP? If so, no need to resolve it. */
3457         if (insp_aton(x->IPAddr.c_str(), &binip) > 0)
3458         {
3459                 /* Gave a hook, but it wasnt one we know */
3460                 if ((!x->Hook.empty()) && (Utils->hooks.find(x->Hook.c_str()) == Utils->hooks.end()))
3461                         return;
3462                 TreeSocket* newsocket = new TreeSocket(Utils, ServerInstance, x->IPAddr,x->Port,false,x->Timeout ? x->Timeout : 10,x->Name.c_str(), x->Hook.empty() ? NULL : Utils->hooks[x->Hook.c_str()]);
3463                 if (newsocket->GetFd() > -1)
3464                 {
3465                         /* Handled automatically on success */
3466                 }
3467                 else
3468                 {
3469                         ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(),strerror(errno));
3470                         delete newsocket;
3471                         Utils->DoFailOver(x);
3472                 }
3473         }
3474         else
3475         {
3476                 try
3477                 {
3478                         bool cached;
3479                         ServernameResolver* snr = new ServernameResolver((Module*)this, Utils, ServerInstance,x->IPAddr, *x, cached);
3480                         ServerInstance->AddResolver(snr, cached);
3481                 }
3482                 catch (ModuleException& e)
3483                 {
3484                         ServerInstance->SNO->WriteToSnoMask('l',"CONNECT: Error connecting \002%s\002: %s.",x->Name.c_str(), e.GetReason());
3485                         Utils->DoFailOver(x);
3486                 }
3487         }
3488 }
3489
3490 void ModuleSpanningTree::AutoConnectServers(time_t curtime)
3491 {
3492         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
3493         {
3494                 if ((x->AutoConnect) && (curtime >= x->NextConnectTime))
3495                 {
3496                         x->NextConnectTime = curtime + x->AutoConnect;
3497                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
3498                         if (x->FailOver.length())
3499                         {
3500                                 TreeServer* CheckFailOver = Utils->FindServer(x->FailOver.c_str());
3501                                 if (CheckFailOver)
3502                                 {
3503                                         /* The failover for this server is currently a member of the network.
3504                                          * The failover probably succeeded, where the main link did not.
3505                                          * Don't try the main link until the failover is gone again.
3506                                          */
3507                                         continue;
3508                                 }
3509                         }
3510                         if (!CheckDupe)
3511                         {
3512                                 // an autoconnected server is not connected. Check if its time to connect it
3513                                 ServerInstance->SNO->WriteToSnoMask('l',"AUTOCONNECT: Auto-connecting server \002%s\002 (%lu seconds until next attempt)",x->Name.c_str(),x->AutoConnect);
3514                                 this->ConnectServer(&(*x));
3515                         }
3516                 }
3517         }
3518 }
3519
3520 int ModuleSpanningTree::HandleVersion(const char** parameters, int pcnt, userrec* user)
3521 {
3522         // we've already checked if pcnt > 0, so this is safe
3523         TreeServer* found = Utils->FindServerMask(parameters[0]);
3524         if (found)
3525         {
3526                 std::string Version = found->GetVersion();
3527                 user->WriteServ("351 %s :%s",user->nick,Version.c_str());
3528                 if (found == Utils->TreeRoot)
3529                 {
3530                         ServerInstance->Config->Send005(user);
3531                 }
3532         }
3533         else
3534         {
3535                 user->WriteServ("402 %s %s :No such server",user->nick,parameters[0]);
3536         }
3537         return 1;
3538 }
3539         
3540 int ModuleSpanningTree::HandleConnect(const char** parameters, int pcnt, userrec* user)
3541 {
3542         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
3543         {
3544                 if (ServerInstance->MatchText(x->Name.c_str(),parameters[0]))
3545                 {
3546                         TreeServer* CheckDupe = Utils->FindServer(x->Name.c_str());
3547                         if (!CheckDupe)
3548                         {
3549                                 user->WriteServ("NOTICE %s :*** CONNECT: Connecting to server: \002%s\002 (%s:%d)",user->nick,x->Name.c_str(),(x->HiddenFromStats ? "<hidden>" : x->IPAddr.c_str()),x->Port);
3550                                 ConnectServer(&(*x));
3551                                 return 1;
3552                         }
3553                         else
3554                         {
3555                                 user->WriteServ("NOTICE %s :*** CONNECT: Server \002%s\002 already exists on the network and is connected via \002%s\002",user->nick,x->Name.c_str(),CheckDupe->GetParent()->GetName().c_str());
3556                                 return 1;
3557                         }
3558                 }
3559         }
3560         user->WriteServ("NOTICE %s :*** CONNECT: No server matching \002%s\002 could be found in the config file.",user->nick,parameters[0]);
3561         return 1;
3562 }
3563
3564 void ModuleSpanningTree::BroadcastTimeSync()
3565 {
3566         std::deque<std::string> params;
3567         params.push_back(ConvToStr(ServerInstance->Time(true)));
3568         Utils->DoOneToMany(Utils->TreeRoot->GetName(), "TIMESET", params);
3569 }
3570
3571 int ModuleSpanningTree::OnStats(char statschar, userrec* user, string_list &results)
3572 {
3573         if ((statschar == 'c') || (statschar == 'n'))
3574         {
3575                 for (unsigned int i = 0; i < Utils->LinkBlocks.size(); i++)
3576                 {
3577                         results.push_back(std::string(ServerInstance->Config->ServerName)+" 213 "+user->nick+" "+statschar+" *@"+(Utils->LinkBlocks[i].HiddenFromStats ? "<hidden>" : Utils->LinkBlocks[i].IPAddr)+" * "+Utils->LinkBlocks[i].Name.c_str()+" "+ConvToStr(Utils->LinkBlocks[i].Port)+" "+(Utils->LinkBlocks[i].Hook.empty() ? "plaintext" : Utils->LinkBlocks[i].Hook)+" "+(Utils->LinkBlocks[i].AutoConnect ? 'a' : '-')+'s');
3578                         if (statschar == 'c')
3579                                 results.push_back(std::string(ServerInstance->Config->ServerName)+" 244 "+user->nick+" H * * "+Utils->LinkBlocks[i].Name.c_str());
3580                 }
3581                 results.push_back(std::string(ServerInstance->Config->ServerName)+" 219 "+user->nick+" "+statschar+" :End of /STATS report");
3582                 ServerInstance->SNO->WriteToSnoMask('t',"Notice: %s '%c' requested by %s (%s@%s)",(!strcmp(user->server,ServerInstance->Config->ServerName) ? "Stats" : "Remote stats"),statschar,user->nick,user->ident,user->host);
3583                 return 1;
3584         }
3585         return 0;
3586 }
3587
3588 int ModuleSpanningTree::OnPreCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, bool validated, const std::string &original_line)
3589 {
3590         /* If the command doesnt appear to be valid, we dont want to mess with it. */
3591         if (!validated)
3592                 return 0;
3593         if (command == "CONNECT")
3594         {
3595                 return this->HandleConnect(parameters,pcnt,user);
3596         }
3597         else if (command == "STATS")
3598         {
3599                 return this->HandleStats(parameters,pcnt,user);
3600         }
3601         else if (command == "MOTD")
3602         {
3603                 return this->HandleMotd(parameters,pcnt,user);
3604         }
3605         else if (command == "ADMIN")
3606         {
3607                 return this->HandleAdmin(parameters,pcnt,user);
3608         }
3609         else if (command == "SQUIT")
3610         {
3611                 return this->HandleSquit(parameters,pcnt,user);
3612         }
3613         else if (command == "MAP")
3614         {
3615                 this->HandleMap(parameters,pcnt,user);
3616                 return 1;
3617         }
3618         else if ((command == "TIME") && (pcnt > 0))
3619         {
3620                 return this->HandleTime(parameters,pcnt,user);
3621         }
3622         else if (command == "LUSERS")
3623         {
3624                 this->HandleLusers(parameters,pcnt,user);
3625                 return 1;
3626         }
3627         else if (command == "LINKS")
3628         {
3629                 this->HandleLinks(parameters,pcnt,user);
3630                 return 1;
3631         }
3632         else if (command == "WHOIS")
3633         {
3634                 if (pcnt > 1)
3635                 {
3636                         // remote whois
3637                         return this->HandleRemoteWhois(parameters,pcnt,user);
3638                 }
3639         }
3640         else if ((command == "VERSION") && (pcnt > 0))
3641         {
3642                 this->HandleVersion(parameters,pcnt,user);
3643                 return 1;
3644         }
3645         return 0;
3646 }
3647
3648 void ModuleSpanningTree::OnPostCommand(const std::string &command, const char** parameters, int pcnt, userrec *user, CmdResult result, const std::string &original_line)
3649 {
3650         if ((result == CMD_SUCCESS) && (ServerInstance->IsValidModuleCommand(command, pcnt, user)))
3651         {
3652                 // this bit of code cleverly routes all module commands
3653                 // to all remote severs *automatically* so that modules
3654                 // can just handle commands locally, without having
3655                 // to have any special provision in place for remote
3656                 // commands and linking protocols.
3657                 std::deque<std::string> params;
3658                 params.clear();
3659                 for (int j = 0; j < pcnt; j++)
3660                 {
3661                         if (strchr(parameters[j],' '))
3662                         {
3663                                 params.push_back(":" + std::string(parameters[j]));
3664                         }
3665                         else
3666                         {
3667                                 params.push_back(std::string(parameters[j]));
3668                         }
3669                 }
3670                 Utils->DoOneToMany(user->nick,command,params);
3671         }
3672 }
3673
3674 void ModuleSpanningTree::OnGetServerDescription(const std::string &servername,std::string &description)
3675 {
3676         TreeServer* s = Utils->FindServer(servername);
3677         if (s)
3678         {
3679                 description = s->GetDesc();
3680         }
3681 }
3682
3683 void ModuleSpanningTree::OnUserInvite(userrec* source,userrec* dest,chanrec* channel)
3684 {
3685         if (IS_LOCAL(source))
3686         {
3687                 std::deque<std::string> params;
3688                 params.push_back(dest->nick);
3689                 params.push_back(channel->name);
3690                 Utils->DoOneToMany(source->nick,"INVITE",params);
3691         }
3692 }
3693
3694 void ModuleSpanningTree::OnPostLocalTopicChange(userrec* user, chanrec* chan, const std::string &topic)
3695 {
3696         std::deque<std::string> params;
3697         params.push_back(chan->name);
3698         params.push_back(":"+topic);
3699         Utils->DoOneToMany(user->nick,"TOPIC",params);
3700 }
3701
3702 void ModuleSpanningTree::OnWallops(userrec* user, const std::string &text)
3703 {
3704         if (IS_LOCAL(user))
3705         {
3706                 std::deque<std::string> params;
3707                 params.push_back(":"+text);
3708                 Utils->DoOneToMany(user->nick,"WALLOPS",params);
3709         }
3710 }
3711
3712 void ModuleSpanningTree::OnUserNotice(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
3713 {
3714         if (target_type == TYPE_USER)
3715         {
3716                 userrec* d = (userrec*)dest;
3717                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
3718                 {
3719                         std::deque<std::string> params;
3720                         params.clear();
3721                         params.push_back(d->nick);
3722                         params.push_back(":"+text);
3723                         Utils->DoOneToOne(user->nick,"NOTICE",params,d->server);
3724                 }
3725         }
3726         else if (target_type == TYPE_CHANNEL)
3727         {
3728                 if (IS_LOCAL(user))
3729                 {
3730                         chanrec *c = (chanrec*)dest;
3731                         if (c)
3732                         {
3733                                 std::string cname = c->name;
3734                                 if (status)
3735                                         cname = status + cname;
3736                                 TreeServerList list;
3737                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
3738                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
3739                                 {
3740                                         TreeSocket* Sock = i->second->GetSocket();
3741                                         if (Sock)
3742                                                 Sock->WriteLine(":"+std::string(user->nick)+" NOTICE "+cname+" :"+text);
3743                                 }
3744                         }
3745                 }
3746         }
3747         else if (target_type == TYPE_SERVER)
3748         {
3749                 if (IS_LOCAL(user))
3750                 {
3751                         char* target = (char*)dest;
3752                         std::deque<std::string> par;
3753                         par.push_back(target);
3754                         par.push_back(":"+text);
3755                         Utils->DoOneToMany(user->nick,"NOTICE",par);
3756                 }
3757         }
3758 }
3759
3760 void ModuleSpanningTree::OnUserMessage(userrec* user, void* dest, int target_type, const std::string &text, char status, const CUList &exempt_list)
3761 {
3762         if (target_type == TYPE_USER)
3763         {
3764                 // route private messages which are targetted at clients only to the server
3765                 // which needs to receive them
3766                 userrec* d = (userrec*)dest;
3767                 if ((d->GetFd() < 0) && (IS_LOCAL(user)))
3768                 {
3769                         std::deque<std::string> params;
3770                         params.clear();
3771                         params.push_back(d->nick);
3772                         params.push_back(":"+text);
3773                         Utils->DoOneToOne(user->nick,"PRIVMSG",params,d->server);
3774                 }
3775         }
3776         else if (target_type == TYPE_CHANNEL)
3777         {
3778                 if (IS_LOCAL(user))
3779                 {
3780                         chanrec *c = (chanrec*)dest;
3781                         if (c)
3782                         {
3783                                 std::string cname = c->name;
3784                                 if (status)
3785                                         cname = status + cname;
3786                                 TreeServerList list;
3787                                 Utils->GetListOfServersForChannel(c,list,status,exempt_list);
3788                                 for (TreeServerList::iterator i = list.begin(); i != list.end(); i++)
3789                                 {
3790                                         TreeSocket* Sock = i->second->GetSocket();
3791                                         if (Sock)
3792                                                 Sock->WriteLine(":"+std::string(user->nick)+" PRIVMSG "+cname+" :"+text);
3793                                 }
3794                         }
3795                 }
3796         }
3797         else if (target_type == TYPE_SERVER)
3798         {
3799                 if (IS_LOCAL(user))
3800                 {
3801                         char* target = (char*)dest;
3802                         std::deque<std::string> par;
3803                         par.push_back(target);
3804                         par.push_back(":"+text);
3805                         Utils->DoOneToMany(user->nick,"PRIVMSG",par);
3806                 }
3807         }
3808 }
3809
3810 void ModuleSpanningTree::OnBackgroundTimer(time_t curtime)
3811 {
3812         AutoConnectServers(curtime);
3813         DoPingChecks(curtime);
3814 }
3815
3816 void ModuleSpanningTree::OnUserJoin(userrec* user, chanrec* channel)
3817 {
3818         // Only do this for local users
3819         if (IS_LOCAL(user))
3820         {
3821                 if (channel->GetUserCounter() == 1)
3822                 {
3823                         std::deque<std::string> params;
3824                         // set up their permissions and the channel TS with FJOIN.
3825                         // All users are FJOINed now, because a module may specify
3826                         // new joining permissions for the user.
3827                         params.push_back(channel->name);
3828                         params.push_back(ConvToStr(channel->age));
3829                         params.push_back(std::string(channel->GetAllPrefixChars(user))+","+std::string(user->nick));
3830                         Utils->DoOneToMany(ServerInstance->Config->ServerName,"FJOIN",params);
3831                         /* First user in, sync the modes for the channel */
3832                         params.pop_back();
3833                         /* This is safe, all inspircd servers default to +nt */
3834                         params.push_back("+nt");
3835                         Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",params);
3836                 }
3837                 else
3838                 {
3839                         std::deque<std::string> params;
3840                         params.push_back(channel->name);
3841                         params.push_back(ConvToStr(channel->age));
3842                         Utils->DoOneToMany(user->nick,"JOIN",params);
3843                 }
3844         }
3845 }
3846
3847 void ModuleSpanningTree::OnChangeHost(userrec* user, const std::string &newhost)
3848 {
3849         // only occurs for local clients
3850         if (user->registered != REG_ALL)
3851                 return;
3852         std::deque<std::string> params;
3853         params.push_back(newhost);
3854         Utils->DoOneToMany(user->nick,"FHOST",params);
3855 }
3856
3857 void ModuleSpanningTree::OnChangeName(userrec* user, const std::string &gecos)
3858 {
3859         // only occurs for local clients
3860         if (user->registered != REG_ALL)
3861                 return;
3862         std::deque<std::string> params;
3863         params.push_back(gecos);
3864         Utils->DoOneToMany(user->nick,"FNAME",params);
3865 }
3866
3867 void ModuleSpanningTree::OnUserPart(userrec* user, chanrec* channel, const std::string &partmessage)
3868 {
3869         if (IS_LOCAL(user))
3870         {
3871                 std::deque<std::string> params;
3872                 params.push_back(channel->name);
3873                 if (partmessage != "")
3874                         params.push_back(":"+partmessage);
3875                 Utils->DoOneToMany(user->nick,"PART",params);
3876         }
3877 }
3878
3879 void ModuleSpanningTree::OnUserConnect(userrec* user)
3880 {
3881         char agestr[MAXBUF];
3882         if (IS_LOCAL(user))
3883         {
3884                 std::deque<std::string> params;
3885                 snprintf(agestr,MAXBUF,"%lu",(unsigned long)user->age);
3886                 params.push_back(agestr);
3887                 params.push_back(user->nick);
3888                 params.push_back(user->host);
3889                 params.push_back(user->dhost);
3890                 params.push_back(user->ident);
3891                 params.push_back("+"+std::string(user->FormatModes()));
3892                 params.push_back(user->GetIPString());
3893                 params.push_back(":"+std::string(user->fullname));
3894                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"NICK",params);
3895                 // User is Local, change needs to be reflected!
3896                 TreeServer* SourceServer = Utils->FindServer(user->server);
3897                 if (SourceServer)
3898                 {
3899                         SourceServer->AddUserCount();
3900                 }
3901         }
3902 }
3903
3904 void ModuleSpanningTree::OnUserQuit(userrec* user, const std::string &reason)
3905 {
3906         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
3907         {
3908                 std::deque<std::string> params;
3909                 params.push_back(":"+reason);
3910                 Utils->DoOneToMany(user->nick,"QUIT",params);
3911         }
3912         // Regardless, We need to modify the user Counts..
3913         TreeServer* SourceServer = Utils->FindServer(user->server);
3914         if (SourceServer)
3915         {
3916                 SourceServer->DelUserCount();
3917         }
3918 }
3919
3920 void ModuleSpanningTree::OnUserPostNick(userrec* user, const std::string &oldnick)
3921 {
3922         if (IS_LOCAL(user))
3923         {
3924                 std::deque<std::string> params;
3925                 params.push_back(user->nick);
3926                 Utils->DoOneToMany(oldnick,"NICK",params);
3927         }
3928 }
3929
3930 void ModuleSpanningTree::OnUserKick(userrec* source, userrec* user, chanrec* chan, const std::string &reason)
3931 {
3932         if ((source) && (IS_LOCAL(source)))
3933         {
3934                 std::deque<std::string> params;
3935                 params.push_back(chan->name);
3936                 params.push_back(user->nick);
3937                 params.push_back(":"+reason);
3938                 Utils->DoOneToMany(source->nick,"KICK",params);
3939         }
3940         else if (!source)
3941         {
3942                 std::deque<std::string> params;
3943                 params.push_back(chan->name);
3944                 params.push_back(user->nick);
3945                 params.push_back(":"+reason);
3946                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"KICK",params);
3947         }
3948 }
3949
3950 void ModuleSpanningTree::OnRemoteKill(userrec* source, userrec* dest, const std::string &reason)
3951 {
3952         std::deque<std::string> params;
3953         params.push_back(dest->nick);
3954         params.push_back(":"+reason);
3955         Utils->DoOneToMany(source->nick,"KILL",params);
3956 }
3957
3958 void ModuleSpanningTree::OnRehash(userrec* user, const std::string &parameter)
3959 {
3960         if (parameter != "")
3961         {
3962                 std::deque<std::string> params;
3963                 params.push_back(parameter);
3964                 Utils->DoOneToMany(user ? user->nick : ServerInstance->Config->ServerName, "REHASH", params);
3965                 // check for self
3966                 if (ServerInstance->MatchText(ServerInstance->Config->ServerName,parameter))
3967                 {
3968                         ServerInstance->WriteOpers("*** Remote rehash initiated locally by \002%s\002", user ? user->nick : ServerInstance->Config->ServerName);
3969                         ServerInstance->RehashServer();
3970                 }
3971         }
3972         Utils->ReadConfiguration(false);
3973         InitializeDisabledCommands(ServerInstance->Config->DisabledCommands, ServerInstance);
3974 }
3975
3976 // note: the protocol does not allow direct umode +o except
3977 // via NICK with 8 params. sending OPERTYPE infers +o modechange
3978 // locally.
3979 void ModuleSpanningTree::OnOper(userrec* user, const std::string &opertype)
3980 {
3981         if (IS_LOCAL(user))
3982         {
3983                 std::deque<std::string> params;
3984                 params.push_back(opertype);
3985                 Utils->DoOneToMany(user->nick,"OPERTYPE",params);
3986         }
3987 }
3988
3989 void ModuleSpanningTree::OnLine(userrec* source, const std::string &host, bool adding, char linetype, long duration, const std::string &reason)
3990 {
3991         if (!source)
3992         {
3993                 /* Server-set lines */
3994                 char data[MAXBUF];
3995                 snprintf(data,MAXBUF,"%c %s %s %lu %lu :%s", linetype, host.c_str(), ServerInstance->Config->ServerName, (unsigned long)ServerInstance->Time(false),
3996                                 (unsigned long)duration, reason.c_str());
3997                 std::deque<std::string> params;
3998                 params.push_back(data);
3999                 Utils->DoOneToMany(ServerInstance->Config->ServerName, "ADDLINE", params);
4000         }
4001         else
4002         {
4003                 if (IS_LOCAL(source))
4004                 {
4005                         char type[8];
4006                         snprintf(type,8,"%cLINE",linetype);
4007                         std::string stype = type;
4008                         if (adding)
4009                         {
4010                                 char sduration[MAXBUF];
4011                                 snprintf(sduration,MAXBUF,"%ld",duration);
4012                                 std::deque<std::string> params;
4013                                 params.push_back(host);
4014                                 params.push_back(sduration);
4015                                 params.push_back(":"+reason);
4016                                 Utils->DoOneToMany(source->nick,stype,params);
4017                         }
4018                         else
4019                         {
4020                                 std::deque<std::string> params;
4021                                 params.push_back(host);
4022                                 Utils->DoOneToMany(source->nick,stype,params);
4023                         }
4024                 }
4025         }
4026 }
4027
4028 void ModuleSpanningTree::OnAddGLine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
4029 {
4030         OnLine(source,hostmask,true,'G',duration,reason);
4031 }
4032         
4033 void ModuleSpanningTree::OnAddZLine(long duration, userrec* source, const std::string &reason, const std::string &ipmask)
4034 {
4035         OnLine(source,ipmask,true,'Z',duration,reason);
4036 }
4037
4038 void ModuleSpanningTree::OnAddQLine(long duration, userrec* source, const std::string &reason, const std::string &nickmask)
4039 {
4040         OnLine(source,nickmask,true,'Q',duration,reason);
4041 }
4042
4043 void ModuleSpanningTree::OnAddELine(long duration, userrec* source, const std::string &reason, const std::string &hostmask)
4044 {
4045         OnLine(source,hostmask,true,'E',duration,reason);
4046 }
4047
4048 void ModuleSpanningTree::OnDelGLine(userrec* source, const std::string &hostmask)
4049 {
4050         OnLine(source,hostmask,false,'G',0,"");
4051 }
4052
4053 void ModuleSpanningTree::OnDelZLine(userrec* source, const std::string &ipmask)
4054 {
4055         OnLine(source,ipmask,false,'Z',0,"");
4056 }
4057
4058 void ModuleSpanningTree::OnDelQLine(userrec* source, const std::string &nickmask)
4059 {
4060         OnLine(source,nickmask,false,'Q',0,"");
4061 }
4062
4063 void ModuleSpanningTree::OnDelELine(userrec* source, const std::string &hostmask)
4064 {
4065         OnLine(source,hostmask,false,'E',0,"");
4066 }
4067
4068 void ModuleSpanningTree::OnMode(userrec* user, void* dest, int target_type, const std::string &text)
4069 {
4070         if ((IS_LOCAL(user)) && (user->registered == REG_ALL))
4071         {
4072                 if (target_type == TYPE_USER)
4073                 {
4074                         userrec* u = (userrec*)dest;
4075                         std::deque<std::string> params;
4076                         params.push_back(u->nick);
4077                         params.push_back(text);
4078                         Utils->DoOneToMany(user->nick,"MODE",params);
4079                 }
4080                 else
4081                 {
4082                         chanrec* c = (chanrec*)dest;
4083                         std::deque<std::string> params;
4084                         params.push_back(c->name);
4085                         params.push_back(text);
4086                         Utils->DoOneToMany(user->nick,"MODE",params);
4087                 }
4088         }
4089 }
4090
4091 void ModuleSpanningTree::OnSetAway(userrec* user)
4092 {
4093         if (IS_LOCAL(user))
4094         {
4095                 std::deque<std::string> params;
4096                 params.push_back(":"+std::string(user->awaymsg));
4097                 Utils->DoOneToMany(user->nick,"AWAY",params);
4098         }
4099 }
4100
4101 void ModuleSpanningTree::OnCancelAway(userrec* user)
4102 {
4103         if (IS_LOCAL(user))
4104         {
4105                 std::deque<std::string> params;
4106                 params.clear();
4107                 Utils->DoOneToMany(user->nick,"AWAY",params);
4108         }
4109 }
4110
4111 void ModuleSpanningTree::ProtoSendMode(void* opaque, int target_type, void* target, const std::string &modeline)
4112 {
4113         TreeSocket* s = (TreeSocket*)opaque;
4114         if (target)
4115         {
4116                 if (target_type == TYPE_USER)
4117                 {
4118                         userrec* u = (userrec*)target;
4119                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+u->nick+" "+ConvToStr(u->age)+" "+modeline);
4120                 }
4121                 else
4122                 {
4123                         chanrec* c = (chanrec*)target;
4124                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" FMODE "+c->name+" "+ConvToStr(c->age)+" "+modeline);
4125                 }
4126         }
4127 }
4128
4129 void ModuleSpanningTree::ProtoSendMetaData(void* opaque, int target_type, void* target, const std::string &extname, const std::string &extdata)
4130 {
4131         TreeSocket* s = (TreeSocket*)opaque;
4132         if (target)
4133         {
4134                 if (target_type == TYPE_USER)
4135                 {
4136                         userrec* u = (userrec*)target;
4137                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+u->nick+" "+extname+" :"+extdata);
4138                 }
4139                 else if (target_type == TYPE_CHANNEL)
4140                 {
4141                         chanrec* c = (chanrec*)target;
4142                         s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA "+c->name+" "+extname+" :"+extdata);
4143                 }
4144         }
4145         if (target_type == TYPE_OTHER)
4146         {
4147                 s->WriteLine(std::string(":")+ServerInstance->Config->ServerName+" METADATA * "+extname+" :"+extdata);
4148         }
4149 }
4150
4151 void ModuleSpanningTree::OnEvent(Event* event)
4152 {
4153         std::deque<std::string>* params = (std::deque<std::string>*)event->GetData();
4154         if (event->GetEventID() == "send_metadata")
4155         {
4156                 if (params->size() < 3)
4157                         return;
4158                 (*params)[2] = ":" + (*params)[2];
4159                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"METADATA",*params);
4160         }
4161         else if (event->GetEventID() == "send_topic")
4162         {
4163                 if (params->size() < 2)
4164                         return;
4165                 (*params)[1] = ":" + (*params)[1];
4166                 params->insert(params->begin() + 1,ServerInstance->Config->ServerName);
4167                 params->insert(params->begin() + 1,ConvToStr(ServerInstance->Time(true)));
4168                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FTOPIC",*params);
4169         }
4170         else if (event->GetEventID() == "send_mode")
4171         {
4172                 if (params->size() < 2)
4173                         return;
4174                 // Insert the TS value of the object, either userrec or chanrec
4175                 time_t ourTS = 0;
4176                 userrec* a = ServerInstance->FindNick((*params)[0]);
4177                 if (a)
4178                 {
4179                         ourTS = a->age;
4180                 }
4181                 else
4182                 {
4183                         chanrec* a = ServerInstance->FindChan((*params)[0]);
4184                         if (a)
4185                         {
4186                                 ourTS = a->age;
4187                         }
4188                 }
4189                 params->insert(params->begin() + 1,ConvToStr(ourTS));
4190                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"FMODE",*params);
4191         }
4192         else if (event->GetEventID() == "send_mode_explicit")
4193         {
4194                 if (params->size() < 2)
4195                         return;
4196                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODE",*params);
4197         }
4198         else if (event->GetEventID() == "send_opers")
4199         {
4200                 if (params->size() < 1)
4201                         return;
4202                 (*params)[0] = ":" + (*params)[0];
4203                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"OPERNOTICE",*params);
4204         }
4205         else if (event->GetEventID() == "send_modeset")
4206         {
4207                 if (params->size() < 2)
4208                         return;
4209                 (*params)[1] = ":" + (*params)[1];
4210                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"MODENOTICE",*params);
4211         }
4212         else if (event->GetEventID() == "send_snoset")
4213         {
4214                 if (params->size() < 2)
4215                         return;
4216                 (*params)[1] = ":" + (*params)[1];
4217                 Utils->DoOneToMany(ServerInstance->Config->ServerName,"SNONOTICE",*params);
4218         }
4219         else if (event->GetEventID() == "send_push")
4220         {
4221                 if (params->size() < 2)
4222                         return;
4223                         
4224                 userrec *a = ServerInstance->FindNick((*params)[0]);
4225                         
4226                 if (!a)
4227                         return;
4228                         
4229                 (*params)[1] = ":" + (*params)[1];
4230                 Utils->DoOneToOne(ServerInstance->Config->ServerName, "PUSH", *params, a->server);
4231         }
4232 }
4233
4234 ModuleSpanningTree::~ModuleSpanningTree()
4235 {
4236         /* This will also free the listeners */
4237         delete Utils;
4238         if (SyncTimer)
4239                 ServerInstance->Timers->DelTimer(SyncTimer);
4240
4241         ServerInstance->DoneWithInterface("InspSocketHook");
4242 }
4243
4244 Version ModuleSpanningTree::GetVersion()
4245 {
4246         return Version(1,1,0,2,VF_VENDOR,API_VERSION);
4247 }
4248
4249 void ModuleSpanningTree::Implements(char* List)
4250 {
4251         List[I_OnPreCommand] = List[I_OnGetServerDescription] = List[I_OnUserInvite] = List[I_OnPostLocalTopicChange] = 1;
4252         List[I_OnWallops] = List[I_OnUserNotice] = List[I_OnUserMessage] = List[I_OnBackgroundTimer] = 1;
4253         List[I_OnUserJoin] = List[I_OnChangeHost] = List[I_OnChangeName] = List[I_OnUserPart] = List[I_OnUserConnect] = 1;
4254         List[I_OnUserQuit] = List[I_OnUserPostNick] = List[I_OnUserKick] = List[I_OnRemoteKill] = List[I_OnRehash] = 1;
4255         List[I_OnOper] = List[I_OnAddGLine] = List[I_OnAddZLine] = List[I_OnAddQLine] = List[I_OnAddELine] = 1;
4256         List[I_OnDelGLine] = List[I_OnDelZLine] = List[I_OnDelQLine] = List[I_OnDelELine] = List[I_ProtoSendMode] = List[I_OnMode] = 1;
4257         List[I_OnStats] = List[I_ProtoSendMetaData] = List[I_OnEvent] = List[I_OnSetAway] = List[I_OnCancelAway] = List[I_OnPostCommand] = 1;
4258 }
4259
4260 /* It is IMPORTANT that m_spanningtree is the last module in the chain
4261  * so that any activity it sees is FINAL, e.g. we arent going to send out
4262  * a NICK message before m_cloaking has finished putting the +x on the user,
4263  * etc etc.
4264  * Therefore, we return PRIORITY_LAST to make sure we end up at the END of
4265  * the module call queue.
4266  */
4267 Priority ModuleSpanningTree::Prioritize()
4268 {
4269         return PRIORITY_LAST;
4270 }
4271
4272 TimeSyncTimer::TimeSyncTimer(InspIRCd *Inst, ModuleSpanningTree *Mod) : InspTimer(43200, Inst->Time(), true), Instance(Inst), Module(Mod)
4273 {
4274 }
4275
4276 void TimeSyncTimer::Tick(time_t TIME)
4277 {
4278         Module->BroadcastTimeSync();
4279 }
4280
4281
4282 class ModuleSpanningTreeFactory : public ModuleFactory
4283 {
4284  public:
4285         ModuleSpanningTreeFactory()
4286         {
4287         }
4288         
4289         ~ModuleSpanningTreeFactory()
4290         {
4291         }
4292         
4293         virtual Module * CreateModule(InspIRCd* Me)
4294         {
4295                 return new ModuleSpanningTree(Me);
4296         }
4297         
4298 };
4299
4300
4301 extern "C" void * init_module( void )
4302 {
4303         return new ModuleSpanningTreeFactory;
4304 }