]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/treesocket2.cpp
f3eff8d74d3d460ea868e7a7a0900edfb97e29ef
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / treesocket2.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 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 #include "inspircd.h"
15 #include "commands/cmd_whois.h"
16 #include "commands/cmd_stats.h"
17 #include "socket.h"
18 #include "wildcard.h"
19 #include "xline.h"
20 #include "transport.h"
21 #include "socketengine.h"
22
23 #include "m_spanningtree/main.h"
24 #include "m_spanningtree/utils.h"
25 #include "m_spanningtree/treeserver.h"
26 #include "m_spanningtree/link.h"
27 #include "m_spanningtree/treesocket.h"
28 #include "m_spanningtree/resolvers.h"
29 #include "m_spanningtree/handshaketimer.h"
30
31 /* $ModDep: m_spanningtree/timesynctimer.h m_spanningtree/resolvers.h m_spanningtree/main.h m_spanningtree/utils.h m_spanningtree/treeserver.h m_spanningtree/link.h m_spanningtree/treesocket.h */
32
33 static std::map<std::string, std::string> warned;       /* Server names that have had protocol violation warnings displayed for them */
34
35 int TreeSocket::WriteLine(std::string line)
36 {
37         Instance->Log(DEBUG, "S[%d] O %s", this->GetFd(), line.c_str());
38         line.append("\r\n");
39         return this->Write(line);
40 }
41
42
43 /* Handle ERROR command */
44 bool TreeSocket::Error(std::deque<std::string> &params)
45 {
46         if (params.size() < 1)
47                 return false;
48         this->Instance->SNO->WriteToSnoMask('l',"ERROR from %s: %s",(!InboundServerName.empty() ? InboundServerName.c_str() : myhost.c_str()),params[0].c_str());
49         /* we will return false to cause the socket to close. */
50         return false;
51 }
52
53 bool TreeSocket::Modules(const std::string &prefix, std::deque<std::string> &params)
54 {
55         if (params.empty())
56                 return true;
57
58         if (!this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
59         {
60                 /* Pass it on, not for us */
61                 Utils->DoOneToOne(prefix, "MODULES", params, params[0]);
62                 return true;
63         }
64
65         char strbuf[MAXBUF];
66         std::deque<std::string> par;
67         par.push_back(prefix);
68         par.push_back("");
69
70         User* source = this->Instance->FindNick(prefix);
71         if (!source)
72                 return true;
73
74         std::vector<std::string> module_names = Instance->Modules->GetAllModuleNames(0);
75
76         for (unsigned int i = 0; i < module_names.size(); i++)
77         {
78                 Module* m = Instance->Modules->Find(module_names[i]);
79                 Version V = m->GetVersion();
80                 char modulename[MAXBUF];
81                 char flagstate[MAXBUF];
82                 *flagstate = 0;
83                 if (V.Flags & VF_STATIC)
84                         strlcat(flagstate,", static",MAXBUF);
85                 if (V.Flags & VF_VENDOR)
86                         strlcat(flagstate,", vendor",MAXBUF);
87                 if (V.Flags & VF_COMMON)
88                         strlcat(flagstate,", common",MAXBUF);
89                 if (V.Flags & VF_SERVICEPROVIDER)
90                         strlcat(flagstate,", service provider",MAXBUF);
91                 if (!flagstate[0])
92                         strcpy(flagstate,"  <no flags>");
93                 strlcpy(modulename,module_names[i].c_str(),256);
94                 if (*source->oper)
95                 {
96                         snprintf(strbuf, MAXBUF, "::%s 900 %s :0x%08lx %d.%d.%d.%d %s (%s)",Instance->Config->ServerName,source->nick,(unsigned long)m,
97                                         V.Major,V.Minor,V.Revision,V.Build,ServerConfig::CleanFilename(modulename),flagstate+2);
98                 }
99                 else
100                 {
101                         snprintf(strbuf, MAXBUF, "::%s 900 %s :%s",Instance->Config->ServerName,source->nick,ServerConfig::CleanFilename(modulename));
102                 }
103                 par[1] = strbuf;
104                 Utils->DoOneToOne(Instance->Config->GetSID(), "PUSH", par, source->server);
105         }
106         snprintf(strbuf, MAXBUF, "::%s 901 %s :End of MODULES list", Instance->Config->ServerName, source->nick);
107         par[1] = strbuf;
108         Utils->DoOneToOne(Instance->Config->GetSID(), "PUSH", par, source->server);
109         return true;
110 }
111
112 /** remote MOTD. leet, huh? */
113 bool TreeSocket::Motd(const std::string &prefix, std::deque<std::string> &params)
114 {
115         if (params.size() > 0)
116         {
117                 if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
118                 {
119                         /* It's for our server */
120                         string_list results;
121                         User* source = this->Instance->FindNick(prefix);
122
123                         if (source)
124                         {
125                                 std::deque<std::string> par;
126                                 par.push_back(prefix);
127                                 par.push_back("");
128
129                                 if (!Instance->Config->MOTD.size())
130                                 {
131                                         par[1] = std::string("::")+Instance->Config->ServerName+" 422 "+source->nick+" :Message of the day file is missing.";
132                                         Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
133                                         return true;
134                                 }
135
136                                 par[1] = std::string("::")+Instance->Config->ServerName+" 375 "+source->nick+" :"+Instance->Config->ServerName+" message of the day";
137                                 Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
138
139                                 for (unsigned int i = 0; i < Instance->Config->MOTD.size(); i++)
140                                 {
141                                         par[1] = std::string("::")+Instance->Config->ServerName+" 372 "+source->nick+" :- "+Instance->Config->MOTD[i];
142                                         Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
143                                 }
144
145                                 par[1] = std::string("::")+Instance->Config->ServerName+" 376 "+source->nick+" :End of message of the day.";
146                                 Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
147                         }
148                 }
149                 else
150                 {
151                         /* Pass it on */
152                         User* source = this->Instance->FindNick(prefix);
153                         if (source)
154                                 Utils->DoOneToOne(prefix, "MOTD", params, params[0]);
155                 }
156         }
157         return true;
158 }
159
160 /** remote ADMIN. leet, huh? */
161 bool TreeSocket::Admin(const std::string &prefix, std::deque<std::string> &params)
162 {
163         if (params.size() > 0)
164         {
165                 if (this->Instance->MatchText(this->Instance->Config->ServerName, params[0]))
166                 {
167                         /* It's for our server */
168                         string_list results;
169                         User* source = this->Instance->FindNick(prefix);
170                         if (source)
171                         {
172                                 std::deque<std::string> par;
173                                 par.push_back(prefix);
174                                 par.push_back("");
175                                 par[1] = std::string("::")+Instance->Config->ServerName+" 256 "+source->nick+" :Administrative info for "+Instance->Config->ServerName;
176                                 Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
177                                 par[1] = std::string("::")+Instance->Config->ServerName+" 257 "+source->nick+" :Name     - "+Instance->Config->AdminName;
178                                 Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
179                                 par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :Nickname - "+Instance->Config->AdminNick;
180                                 Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
181                                 par[1] = std::string("::")+Instance->Config->ServerName+" 258 "+source->nick+" :E-Mail   - "+Instance->Config->AdminEmail;
182                                 Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
183                         }
184                 }
185                 else
186                 {
187                         /* Pass it on */
188                         User* source = this->Instance->FindNick(prefix);
189                         if (source)
190                                 Utils->DoOneToOne(prefix, "ADMIN", params, params[0]);
191                 }
192         }
193         return true;
194 }
195
196 bool TreeSocket::Stats(const std::string &prefix, std::deque<std::string> &params)
197 {
198         /* Get the reply to a STATS query if it matches this servername,
199          * and send it back as a load of PUSH queries
200          */
201         if (params.size() > 1)
202         {
203                 if (this->Instance->MatchText(this->Instance->Config->ServerName, params[1]))
204                 {
205                         /* It's for our server */
206                         string_list results;
207                         User* source = this->Instance->FindNick(prefix);
208                         if (source)
209                         {
210                                 std::deque<std::string> par;
211                                 par.push_back(prefix);
212                                 par.push_back("");
213                                 DoStats(this->Instance, *(params[0].c_str()), source, results);
214                                 for (size_t i = 0; i < results.size(); i++)
215                                 {
216                                         par[1] = "::" + results[i];
217                                         Utils->DoOneToOne(this->Instance->Config->GetSID(), "PUSH",par, source->server);
218                                 }
219                         }
220                 }
221                 else
222                 {
223                         /* Pass it on */
224                         User* source = this->Instance->FindNick(prefix);
225                         if (source)
226                                 Utils->DoOneToOne(source->uuid, "STATS", params, params[1]);
227                 }
228         }
229         return true;
230 }
231
232
233 /** Because the core won't let users or even SERVERS set +o,
234  * we use the OPERTYPE command to do this.
235  */
236 bool TreeSocket::OperType(const std::string &prefix, std::deque<std::string> &params)
237 {
238         if (params.size() != 1)
239                 return true;
240         std::string opertype = params[0];
241         User* u = this->Instance->FindNick(prefix);
242         if (u)
243         {
244                 if (!u->IsModeSet('o'))
245                         this->Instance->Users->all_opers.push_back(u);
246                 u->modes[UM_OPERATOR] = 1;
247                 strlcpy(u->oper,opertype.c_str(),NICKMAX-1);
248                 Utils->DoOneToAllButSender(u->nick,"OPERTYPE",params,u->server);
249
250                 TreeServer* remoteserver = Utils->FindServer(u->server);
251                 bool dosend = true;
252
253                 if (this->Utils->quiet_bursts)
254                 {
255                         /*
256                          * If quiet bursts are enabled, and server is bursting or silent uline (i.e. services),
257                          * then do nothing. -- w00t
258                          */
259                         if (
260                                 remoteserver->bursting ||
261                                 this->Instance->SilentULine(this->Instance->FindServerNamePtr(u->server))
262                            )
263                         {
264                                 dosend = false;
265                         }
266                 }
267
268                 if (dosend)
269                         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()));
270         }
271         return true;
272 }
273
274 /** Because Andy insists that services-compatible servers must
275  * implement SVSNICK and SVSJOIN, that's exactly what we do :p
276  */
277 bool TreeSocket::ForceNick(const std::string &prefix, std::deque<std::string> &params)
278 {
279         if (params.size() < 3)
280                 return true;
281
282         User* u = this->Instance->FindNick(params[0]);
283
284         if (u)
285         {
286                 Utils->DoOneToAllButSender(prefix,"SVSNICK",params,prefix);
287
288                 if (IS_LOCAL(u))
289                 {
290                         std::deque<std::string> par;
291                         par.push_back(params[1]);
292
293                         if (!u->ForceNickChange(params[1].c_str()))
294                         {
295                                 /* buh. UID them */
296                                 if (!u->ForceNickChange(u->uuid))
297                                 {
298                                         User::QuitUser(this->Instance, u, "Nickname collision");
299                                         return true;
300                                 }
301                         }
302
303                         u->age = atoi(params[2].c_str());
304                 }
305         }
306
307         return true;
308 }
309
310 bool TreeSocket::OperQuit(const std::string &prefix, std::deque<std::string> &params)
311 {
312         if (params.size() < 1)
313                 return true;
314
315         User* u = this->Instance->FindNick(prefix);
316
317         if (u)
318         {
319                 u->SetOperQuit(params[0]);
320                 params[0] = ":" + params[0];
321                 Utils->DoOneToAllButSender(prefix,"OPERQUIT",params,prefix);
322         }
323         return true;
324 }
325
326 bool TreeSocket::ServiceJoin(const std::string &prefix, std::deque<std::string> &params)
327 {
328         if (params.size() < 2)
329                 return true;
330
331         if (!this->Instance->IsChannel(params[1].c_str()))
332                 return true;
333
334         User* u = this->Instance->FindNick(params[0]);
335
336         if (u)
337         {
338                 /* only join if it's local, otherwise just pass it on! */
339                 if (IS_LOCAL(u))
340                         Channel::JoinUser(this->Instance, u, params[1].c_str(), false, "", false, Instance->Time());
341                 Utils->DoOneToAllButSender(prefix,"SVSJOIN",params,prefix);
342         }
343         return true;
344 }
345
346 bool TreeSocket::ServicePart(const std::string &prefix, std::deque<std::string> &params)
347 {
348         if (params.size() < 2)
349                 return true;
350
351         if (!this->Instance->IsChannel(params[1].c_str()))
352                 return true;
353
354         User* u = this->Instance->FindNick(params[0]);
355         Channel* c = this->Instance->FindChan(params[1]);
356
357         if (u)
358         {
359                 /* only part if it's local, otherwise just pass it on! */
360                 if (IS_LOCAL(u))
361                         if (!c->PartUser(u, "Services forced part"))
362                                 delete c;
363                 Utils->DoOneToAllButSender(prefix,"SVSPART",params,prefix);
364         }
365
366         return true;
367 }
368
369 bool TreeSocket::RemoteRehash(const std::string &prefix, std::deque<std::string> &params)
370 {
371         if (params.size() < 1)
372                 return false;
373
374         std::string servermask = params[0];
375
376         if (this->Instance->MatchText(this->Instance->Config->ServerName,servermask))
377         {
378                 this->Instance->SNO->WriteToSnoMask('l',"Remote rehash initiated by \002"+prefix+"\002.");
379                 this->Instance->RehashServer();
380                 Utils->ReadConfiguration(true);
381                 InitializeDisabledCommands(Instance->Config->DisabledCommands, Instance);
382         }
383         Utils->DoOneToAllButSender(prefix,"REHASH",params,prefix);
384         return true;
385 }
386
387 bool TreeSocket::RemoteKill(const std::string &prefix, std::deque<std::string> &params)
388 {        
389         if (params.size() != 2)
390                 return true;
391
392         User* who = this->Instance->FindNick(params[0]);
393
394         if (who)
395         {
396                 /* Prepend kill source, if we don't have one */          
397                 if (*(params[1].c_str()) != '[')
398                 {
399                         params[1] = "[" + prefix + "] Killed (" + params[1] +")";
400                 }
401                 std::string reason = params[1];
402                 params[1] = ":" + params[1];
403                 Utils->DoOneToAllButSender(prefix,"KILL",params,prefix);
404                 // NOTE: This is safe with kill hiding on, as RemoteKill is only reached if we have a server prefix.
405                 // in short this is not executed for USERS.
406                 who->Write(":%s KILL %s :%s (%s)", prefix.c_str(), who->nick, prefix.c_str(), reason.c_str());
407                 User::QuitUser(this->Instance,who,reason);
408         }
409         return true;
410 }
411
412 bool TreeSocket::LocalPong(const std::string &prefix, std::deque<std::string> &params)
413 {
414         if (params.size() < 1)
415                 return true;
416
417         if (params.size() == 1)
418         {
419                 TreeServer* ServerSource = Utils->FindServer(prefix);
420                 if (ServerSource)
421                 {
422                         ServerSource->SetPingFlag();
423                         timeval t;
424                         gettimeofday(&t, NULL);
425                         long ts = (t.tv_sec * 1000) + (t.tv_usec / 1000);
426                         ServerSource->rtt = ts - ServerSource->LastPingMsec;
427                 }
428         }
429         else
430         {
431                 std::string forwardto = params[1];
432                 if (forwardto == Instance->Config->GetSID() || forwardto == Instance->Config->ServerName)
433                 {
434                         /*
435                          * this is a PONG for us
436                          * if the prefix is a user, check theyre local, and if they are,
437                          * dump the PONG reply back to their fd. If its a server, do nowt.
438                          * Services might want to send these s->s, but we dont need to yet.
439                          */
440                         User* u = this->Instance->FindNick(prefix);
441                         if (u)
442                         {
443                                 u->WriteServ("PONG %s %s",params[0].c_str(),params[1].c_str());
444                         }
445                 }
446                 else
447                 {
448                         // not for us, pass it on :)
449                         Utils->DoOneToOne(prefix,"PONG",params,forwardto);
450                 }
451         }
452
453         return true;
454 }
455
456 bool TreeSocket::MetaData(const std::string &prefix, std::deque<std::string> &params)
457 {
458         if (params.size() < 2)
459                 return true;
460         else if (params.size() < 3)
461                 params.push_back("");
462         TreeServer* ServerSource = Utils->FindServer(prefix);
463         if (ServerSource)
464         {
465                 if (params[0] == "*")
466                 {
467                         FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_OTHER,NULL,params[1],params[2]));
468                 }
469                 else if (*(params[0].c_str()) == '#')
470                 {
471                         Channel* c = this->Instance->FindChan(params[0]);
472                         if (c)
473                         {
474                                 FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_CHANNEL,c,params[1],params[2]));
475                         }
476                 }
477                 else if (*(params[0].c_str()) != '#')
478                 {
479                         User* u = this->Instance->FindNick(params[0]);
480                         if (u)
481                         {
482                                 FOREACH_MOD_I(this->Instance,I_OnDecodeMetaData,OnDecodeMetaData(TYPE_USER,u,params[1],params[2]));
483                         }
484                 }
485         }
486
487         params[2] = ":" + params[2];
488         Utils->DoOneToAllButSender(prefix,"METADATA",params,prefix);
489         return true;
490 }
491
492 bool TreeSocket::ServerVersion(const std::string &prefix, std::deque<std::string> &params)
493 {
494         if (params.size() < 1)
495                 return true;
496
497         TreeServer* ServerSource = Utils->FindServer(prefix);
498
499         if (ServerSource)
500         {
501                 ServerSource->SetVersion(params[0]);
502         }
503         params[0] = ":" + params[0];
504         Utils->DoOneToAllButSender(prefix,"VERSION",params,prefix);
505         return true;
506 }
507
508 bool TreeSocket::ChangeHost(const std::string &prefix, std::deque<std::string> &params)
509 {
510         if (params.size() < 1)
511                 return true;
512         User* u = this->Instance->FindNick(prefix);
513
514         if (u)
515         {
516                 u->ChangeDisplayedHost(params[0].c_str());
517                 Utils->DoOneToAllButSender(prefix,"FHOST",params,u->server);
518         }
519         return true;
520 }
521
522 bool TreeSocket::AddLine(const std::string &prefix, std::deque<std::string> &params)
523 {
524         if (params.size() < 6)
525         {
526                 this->Instance->SNO->WriteToSnoMask('x',"%s sent me a malformed ADDLINE of type %s.",prefix.c_str(),params[0].c_str());
527                 return true;
528         }
529
530         XLineFactory* xlf = Instance->XLines->GetFactory(params[0]);
531
532         if (!xlf)
533         {
534                 this->Instance->SNO->WriteToSnoMask('x',"%s sent me an unknown ADDLINE type (%s).",prefix.c_str(),params[0].c_str());
535                 return true;
536         }
537
538         XLine* xl = xlf->Generate(Instance->Time(), atoi(params[4].c_str()), params[2].c_str(), params[5].c_str(), params[1].c_str());
539         xl->SetCreateTime(atoi(params[3].c_str()));
540         if (Instance->XLines->AddLine(xl,NULL))
541         {
542                 if (xl->duration)
543                 {
544                         this->Instance->SNO->WriteToSnoMask('x',"%s added %s%s on %s to expire on %s (%s).",prefix.c_str(),params[0].c_str(),params[0].length() == 1 ? "LINE" : "",
545                                         params[1].c_str(),Instance->TimeString(xl->expiry).c_str(),params[5].c_str());
546                 }
547                 else
548                 {
549                         this->Instance->SNO->WriteToSnoMask('x',"%s added permanent %s%s on %s (%s).",prefix.c_str(),params[0].c_str(),params[0].length() == 1 ? "LINE" : "",
550                                         params[1].c_str(),params[5].c_str());
551                 }
552                 params[5] = ":" + params[5];
553
554                 User* u = Instance->FindNick(prefix);
555                 Utils->DoOneToAllButSender(prefix, "ADDLINE", params, u ? u->server : prefix);
556                 TreeServer *remoteserver = Utils->FindServer(u ? u->server : prefix);
557                 
558                 if (!remoteserver->bursting)
559                 {
560                         Instance->XLines->ApplyLines();
561                 }
562         }
563         else
564                 delete xl;
565
566         return true;
567 }
568
569 bool TreeSocket::DelLine(const std::string &prefix, std::deque<std::string> &params)
570 {
571         if (params.size() < 2)
572                 return true;
573
574         User* user = Instance->FindNick(prefix);
575
576         /* NOTE: No check needed on 'user', this function safely handles NULL */
577         if (Instance->XLines->DelLine(params[0].c_str(), params[1], user))
578         {
579                 this->Instance->SNO->WriteToSnoMask('x',"%s removed %s%s on %s.", prefix.c_str(),
580                                 params[0].c_str(), params[0].length() == 1 ? "LINE" : "", params[1].c_str());
581                 Utils->DoOneToAllButSender(prefix,"DELLINE", params, prefix);
582         }
583         return true;
584 }
585
586 bool TreeSocket::ChangeName(const std::string &prefix, std::deque<std::string> &params)
587 {
588         if (params.size() < 1)
589                 return true;
590         User* u = this->Instance->FindNick(prefix);
591         if (u)
592         {
593                 u->ChangeName(params[0].c_str());
594                 params[0] = ":" + params[0];
595                 Utils->DoOneToAllButSender(prefix,"FNAME",params,u->server);
596         }
597         return true;
598 }
599
600 bool TreeSocket::Whois(const std::string &prefix, std::deque<std::string> &params)
601 {
602         if (params.size() < 1)
603                 return true;
604         User* u = this->Instance->FindNick(prefix);
605         if (u)
606         {
607                 // an incoming request
608                 if (params.size() == 1)
609                 {
610                         User* x = this->Instance->FindNick(params[0]);
611                         if ((x) && (IS_LOCAL(x)))
612                         {
613                                 User* x = this->Instance->FindNick(params[0]);
614                                 char signon[MAXBUF];
615                                 char idle[MAXBUF];
616                                 snprintf(signon, MAXBUF, "%lu", (unsigned long)x->signon);
617                                 snprintf(idle, MAXBUF, "%lu", (unsigned long)abs((x->idle_lastmsg) - Instance->Time(true)));
618                                 std::deque<std::string> par;
619                                 par.push_back(prefix);
620                                 par.push_back(signon);
621                                 par.push_back(idle);
622                                 // ours, we're done, pass it BACK
623                                 Utils->DoOneToOne(params[0], "IDLE", par, u->server);
624                         }
625                         else
626                         {
627                                 // not ours pass it on
628                                 if (x)
629                                         Utils->DoOneToOne(prefix, "IDLE", params, x->server);
630                         }
631                 }
632                 else if (params.size() == 3)
633                 {
634                         std::string who_did_the_whois = params[0];
635                         User* who_to_send_to = this->Instance->FindNick(who_did_the_whois);
636                         if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
637                         {
638                                 // an incoming reply to a whois we sent out
639                                 std::string nick_whoised = prefix;
640                                 unsigned long signon = atoi(params[1].c_str());
641                                 unsigned long idle = atoi(params[2].c_str());
642                                 if ((who_to_send_to) && (IS_LOCAL(who_to_send_to)))
643                                 {
644                                         do_whois(this->Instance, who_to_send_to, u, signon, idle, nick_whoised.c_str());
645                                 }
646                         }
647                         else
648                         {
649                                 // not ours, pass it on
650                                 if (who_to_send_to)
651                                         Utils->DoOneToOne(prefix, "IDLE", params, who_to_send_to->server);
652                         }
653                 }
654         }
655         return true;
656 }
657
658 bool TreeSocket::Push(const std::string &prefix, std::deque<std::string> &params)
659 {
660         if (params.size() < 2)
661                 return true;
662         User* u = this->Instance->FindNick(params[0]);
663         if (!u)
664                 return true;
665         if (IS_LOCAL(u))
666         {
667                 u->Write(params[1]);
668         }
669         else
670         {
671                 // continue the raw onwards
672                 params[1] = ":" + params[1];
673                 Utils->DoOneToOne(prefix,"PUSH",params,u->server);
674         }
675         return true;
676 }
677
678 bool TreeSocket::HandleSetTime(const std::string &prefix, std::deque<std::string> &params)
679 {
680         if (!params.size() || !Utils->EnableTimeSync)
681                 return true;
682
683         bool force = false;
684
685         if ((params.size() == 2) && (params[1] == "FORCE"))
686                 force = true;
687
688         time_t them = atoi(params[0].c_str());
689         time_t us = Instance->Time(false);
690
691         time_t diff = them - us;
692
693         Utils->DoOneToAllButSender(prefix, "TIMESET", params, prefix);
694
695         if (force || (them != us))
696         {
697                 time_t old = Instance->SetTimeDelta(diff);
698                 Instance->Log(DEBUG, "TS (diff %d) from %s applied (old delta was %d)", diff, prefix.c_str(), old);
699         }
700
701         return true;
702 }
703
704 bool TreeSocket::Time(const std::string &prefix, std::deque<std::string> &params)
705 {
706         // :source.server TIME remote.server sendernick
707         // :remote.server TIME source.server sendernick TS
708         if (params.size() == 2)
709         {
710                 // someone querying our time?
711                 if (this->Instance->Config->ServerName == params[0] || this->Instance->Config->GetSID() == params[0])
712                 {
713                         User* u = this->Instance->FindNick(params[1]);
714                         if (u)
715                         {
716                                 params.push_back(ConvToStr(Instance->Time(false)));
717                                 params[0] = prefix;
718                                 Utils->DoOneToOne(this->Instance->Config->GetSID(),"TIME",params,params[0]);
719                         }
720                 }
721                 else
722                 {
723                         // not us, pass it on
724                         User* u = this->Instance->FindNick(params[1]);
725                         if (u)
726                                 Utils->DoOneToOne(prefix,"TIME",params,params[0]);
727                 }
728         }
729         else if (params.size() == 3)
730         {
731                 // a response to a previous TIME
732                 User* u = this->Instance->FindNick(params[1]);
733                 if ((u) && (IS_LOCAL(u)))
734                 {
735                         time_t rawtime = atol(params[2].c_str());
736                         struct tm * timeinfo;
737                         timeinfo = localtime(&rawtime);
738                         char tms[26];
739                         snprintf(tms,26,"%s",asctime(timeinfo));
740                         tms[24] = 0;
741                         u->WriteServ("391 %s %s :%s",u->nick,prefix.c_str(),tms);
742                 }
743                 else
744                 {
745                         if (u)
746                                 Utils->DoOneToOne(prefix,"TIME",params,u->server);
747                 }
748         }
749         return true;
750 }
751
752 bool TreeSocket::LocalPing(const std::string &prefix, std::deque<std::string> &params)
753 {
754         if (params.size() < 1)
755                 return true;
756         if (params.size() == 1)
757         {
758                 std::string stufftobounce = params[0];
759                 this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" PONG "+stufftobounce);
760                 return true;
761         }
762         else
763         {
764                 std::string forwardto = params[1];
765                 if (forwardto == this->Instance->Config->ServerName || forwardto == this->Instance->Config->GetSID())
766                 {
767                         // this is a ping for us, send back PONG to the requesting server
768                         params[1] = params[0];
769                         params[0] = forwardto;
770                         Utils->DoOneToOne(forwardto,"PONG",params,params[1]);
771                 }
772                 else
773                 {
774                         // not for us, pass it on :)
775                         Utils->DoOneToOne(prefix,"PING",params,forwardto);
776                 }
777                 return true;
778         }
779 }
780
781 /** TODO: This creates a total mess of output and needs to really use irc::modestacker.
782  */
783 bool TreeSocket::RemoveStatus(const std::string &prefix, std::deque<std::string> &params)
784 {
785         if (params.size() < 1)
786                 return true;
787         Channel* c = Instance->FindChan(params[0]);
788         if (c)
789         {
790                 for (char modeletter = 'A'; modeletter <= 'z'; modeletter++)
791                 {
792                         ModeHandler* mh = Instance->Modes->FindMode(modeletter, MODETYPE_CHANNEL);
793                         if (mh)
794                                 mh->RemoveMode(c);
795                 }
796         }
797         return true;
798 }
799
800 bool TreeSocket::RemoteServer(const std::string &prefix, std::deque<std::string> &params)
801 {
802         if (params.size() < 5)
803         {
804                 SendError("Protocol error - Missing SID");
805                 return false;
806         }
807
808         std::string servername = params[0];
809         std::string password = params[1];
810         // hopcount is not used for a remote server, we calculate this ourselves
811         std::string sid = params[3];
812         std::string description = params[4];
813         TreeServer* ParentOfThis = Utils->FindServer(prefix);
814         if (!ParentOfThis)
815         {
816                 this->SendError("Protocol error - Introduced remote server from unknown server "+prefix);
817                 return false;
818         }
819         if (!this->Instance->IsSID(sid))
820         {
821                 this->SendError("Invalid format server ID: "+sid+"!");
822                 return false;
823         }
824         TreeServer* CheckDupe = Utils->FindServer(servername);
825         if (CheckDupe)
826         {
827                 this->SendError("Server "+servername+" already exists!");
828                 this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, already exists. Closing link with " + prefix);
829                 return false;
830         }
831
832         Link* lnk = Utils->FindLink(servername);
833
834         TreeServer *Node = new TreeServer(this->Utils, this->Instance, servername, description, sid, ParentOfThis,NULL, lnk ? lnk->Hidden : false);
835
836         if (Node->DuplicateID())
837         {
838                 this->SendError("Server ID "+sid+" already exists on the network!");
839                 this->Instance->SNO->WriteToSnoMask('l',"Server \2"+servername+"\2 being introduced from \2" + prefix + "\2 denied, server ID already exists on the network. Closing link with " + prefix);
840                 return false;
841         }
842
843         ParentOfThis->AddChild(Node);
844         params[4] = ":" + params[4];
845         Utils->DoOneToAllButSender(prefix,"SERVER",params,prefix);
846         this->Instance->SNO->WriteToSnoMask('l',"Server \002"+prefix+"\002 introduced server \002"+servername+"\002 ("+description+")");
847         return true;
848 }
849
850 bool TreeSocket::ComparePass(const std::string &ours, const std::string &theirs)
851 {
852         if ((!strncmp(ours.c_str(), "HMAC-SHA256:", 12)) || (!strncmp(theirs.c_str(), "HMAC-SHA256:", 12)))
853         {
854                 /* One or both of us specified hmac sha256, but we don't have sha256 module loaded!
855                  * We can't allow this password as valid.
856                  */
857                 if (!Instance->Modules->Find("m_sha256.so") || !Utils->ChallengeResponse)
858                                 return false;
859                 else
860                         /* Straight string compare of hashes */
861                         return ours == theirs;
862         }
863         else
864                 /* Straight string compare of plaintext */
865                 return ours == theirs;
866 }
867
868 bool TreeSocket::Outbound_Reply_Server(std::deque<std::string> &params)
869 {
870         if (params.size() < 5)
871         {
872                 SendError("Protocol error - Missing SID");
873                 return false;
874         }
875
876         irc::string servername = params[0].c_str();
877         std::string sname = params[0];
878         std::string password = params[1];
879         std::string sid = params[3];
880         std::string description = params[4];
881         int hops = atoi(params[2].c_str());
882
883         this->InboundServerName = sname;
884         this->InboundDescription = description;
885         this->InboundSID = sid;
886
887         if (!sentcapab)
888                 this->SendCapabilities();
889
890         if (hops)
891         {
892                 this->SendError("Server too far away for authentication");
893                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
894                 return false;
895         }
896
897         if (!this->Instance->IsSID(sid))
898         {
899                 this->SendError("Invalid format server ID: "+sid+"!");
900                 return false;
901         }
902
903         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
904         {
905                 if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password)) || (x->RecvPass == password && (this->GetTheirChallenge().empty()))))
906                 {
907                         TreeServer* CheckDupe = Utils->FindServer(sname);
908                         if (CheckDupe)
909                         {
910                                 this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
911                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
912                                 return false;
913                         }
914                         // Begin the sync here. this kickstarts the
915                         // other side, waiting in WAIT_AUTH_2 state,
916                         // into starting their burst, as it shows
917                         // that we're happy.
918                         this->LinkState = CONNECTED;
919                         // we should add the details of this server now
920                         // to the servers tree, as a child of the root
921                         // node.
922
923                         TreeServer *Node = new TreeServer(this->Utils, this->Instance, sname, description, sid, Utils->TreeRoot, this, x->Hidden);
924
925                         if (Node->DuplicateID())
926                         {
927                                 this->SendError("Server ID "+sid+" already exists on the network!");
928                                 this->Instance->SNO->WriteToSnoMask('l',"Server \2"+assign(servername)+"\2 being introduced denied, server ID already exists on the network. Closing link.");
929                                 return false;
930                         }
931
932                         Utils->TreeRoot->AddChild(Node);
933                         params[4] = ":" + params[4];
934                         Utils->DoOneToAllButSender(Instance->Config->GetSID(),"SERVER",params,sname);
935                         Node->bursting = true;
936                         this->DoBurst(Node);
937                         return true;
938                 }
939         }
940         this->SendError("Invalid credentials");
941         this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
942         return false;
943 }
944
945 bool TreeSocket::Inbound_Server(std::deque<std::string> &params)
946 {
947         if (params.size() < 5)
948         {
949                 SendError("Protocol error - Missing SID");
950                 return false;
951         }
952
953         irc::string servername = params[0].c_str();
954         std::string sname = params[0];
955         std::string password = params[1];
956         std::string sid = params[3];
957         std::string description = params[4];
958         int hops = atoi(params[2].c_str());
959
960         this->InboundServerName = sname;
961         this->InboundDescription = description;
962         this->InboundSID = sid;
963
964         if (!sentcapab)
965                 this->SendCapabilities();
966
967         if (hops)
968         {
969                 this->SendError("Server too far away for authentication");
970                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server is too far away for authentication");
971                 return false;
972         }
973
974         if (!this->Instance->IsSID(sid))
975         {
976                 this->SendError("Invalid format server ID: "+sid+"!");
977                 return false;
978         }
979
980         for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
981         {
982                 if ((x->Name == servername) && ((ComparePass(this->MakePass(x->RecvPass,this->GetOurChallenge()),password) || x->RecvPass == password && (this->GetTheirChallenge().empty()))))
983                 {
984                         /* Check for fully initialized instances of the server by id */
985                         Instance->Log(DEBUG,"Looking for dupe SID %s", sid.c_str());
986                         TreeServer* CheckDupeSID = Utils->FindServerID(sid);
987                         if (CheckDupeSID)
988                         {
989                                 this->SendError("Server ID "+CheckDupeSID->GetID()+" already exists on server "+CheckDupeSID->GetName()+"!");
990                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, server ID '"+CheckDupeSID->GetID()+
991                                                 "' already exists on server "+CheckDupeSID->GetName());
992                                 return false;
993                         }
994                         /* Now check for fully initialized instances of the server by name */
995                         TreeServer* CheckDupe = Utils->FindServer(sname);
996                         if (CheckDupe)
997                         {
998                                 this->SendError("Server "+sname+" already exists on server "+CheckDupe->GetParent()->GetName()+"!");
999                                 this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, already exists on server "+CheckDupe->GetParent()->GetName());
1000                                 return false;
1001                         }
1002                         this->Instance->SNO->WriteToSnoMask('l',"Verified incoming server connection from \002"+sname+"\002["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] ("+description+")");
1003                         if (this->Hook)
1004                         {
1005                                 std::string name = BufferedSocketNameRequest((Module*)Utils->Creator, this->Hook).Send();
1006                                 this->Instance->SNO->WriteToSnoMask('l',"Connection from \2"+sname+"\2["+(x->HiddenFromStats ? "<hidden>" : this->GetIP())+"] using transport \2"+name+"\2");
1007                         }
1008
1009                         // this is good. Send our details: Our server name and description and hopcount of 0,
1010                         // along with the sendpass from this block.
1011                         this->WriteLine(std::string("SERVER ")+this->Instance->Config->ServerName+" "+this->MakePass(x->SendPass, this->GetTheirChallenge())+" 0 "+Instance->Config->GetSID()+" :"+this->Instance->Config->ServerDesc);
1012                         // move to the next state, we are now waiting for THEM.
1013                         this->LinkState = WAIT_AUTH_2;
1014                         return true;
1015                 }
1016         }
1017         this->SendError("Invalid credentials");
1018         this->Instance->SNO->WriteToSnoMask('l',"Server connection from \2"+sname+"\2 denied, invalid link credentials");
1019         return false;
1020 }
1021
1022 void TreeSocket::Split(const std::string &line, std::deque<std::string> &n)
1023 {
1024         n.clear();
1025         irc::tokenstream tokens(line);
1026         std::string param;
1027         while (tokens.GetToken(param))
1028         {
1029                 n.push_back(param);
1030         }
1031         return;
1032 }
1033
1034 bool TreeSocket::ProcessLine(std::string &line)
1035 {
1036         std::deque<std::string> params;
1037         irc::string command;
1038         std::string prefix;
1039
1040         line = line.substr(0, line.find_first_of("\r\n"));
1041
1042         if (line.empty())
1043                 return true;
1044
1045         Instance->Log(DEBUG, "S[%d] I %s", this->GetFd(), line.c_str());
1046
1047         this->Split(line.c_str(),params);
1048         
1049         if (params.empty())
1050                 return true;
1051         
1052         if ((params[0][0] == ':') && (params.size() > 1))
1053         {
1054                 prefix = params[0].substr(1);
1055                 params.pop_front();
1056                 
1057                 if (prefix.empty())
1058                 {
1059                         this->SendError("BUG (?) Empty prefix recieved.");
1060                         return false;
1061                 }
1062         }
1063         
1064         command = params[0].c_str();
1065         params.pop_front();
1066
1067         switch (this->LinkState)
1068         {
1069                 TreeServer* Node;
1070
1071                 case WAIT_AUTH_1:
1072                         /*
1073                          * State WAIT_AUTH_1:
1074                          *  Waiting for SERVER command from remote server. Server initiating
1075                          *  the connection sends the first SERVER command, listening server
1076                          *  replies with theirs if its happy, then if the initiator is happy,
1077                          *  it starts to send its net sync, which starts the merge, otherwise
1078                          *  it sends an ERROR.
1079                          */
1080                         if (command == "PASS")
1081                         {
1082                                 /*
1083                                  * Ignore this silently. Some services packages insist on sending PASS, even
1084                                  * when it is not required (i.e. by us). We have to ignore this here, otherwise
1085                                  * as it's an unknown command (effectively), it will cause the connection to be
1086                                  * closed, which probably isn't what people want. -- w00t
1087                                  */
1088                         }
1089                         else if (command == "SERVER")
1090                         {
1091                                 return this->Inbound_Server(params);
1092                         }
1093                         else if (command == "ERROR")
1094                         {
1095                                 return this->Error(params);
1096                         }
1097                         else if (command == "USER")
1098                         {
1099                                 this->SendError("Client connections to this port are prohibited.");
1100                                 return false;
1101                         }
1102                         else if (command == "CAPAB")
1103                         {
1104                                 return this->Capab(params);
1105                         }
1106                         else
1107                         {
1108                                 // XXX ...wtf.
1109                                 irc::string error = "Invalid command in negotiation phase: " + command;
1110                                 this->SendError(assign(error));
1111                                 return false;
1112                         }
1113                 break;
1114                 case WAIT_AUTH_2:
1115                         /*
1116                          * State WAIT_AUTH_2:
1117                          *  We have sent SERVER to the other side of the connection. Now we're waiting for them to start BURST.
1118                          *  The other option at this stage of things, of course, is for them to close our connection thanks
1119                          *  to invalid credentials.. -- w
1120                          */
1121                         if (command == "SERVER")
1122                         {
1123                                 /*
1124                                  * Connection is either attempting to re-auth itself (stupid) or sending netburst without sending BURST.
1125                                  * Both of these aren't allowable, so block them here. -- w
1126                                  */
1127                                 this->SendError("You may not re-authenticate or commence netburst without sending BURST.");
1128                                 return true;
1129                         }
1130                         else if (command == "BURST")
1131                         {
1132                                 if (params.size() && Utils->EnableTimeSync)
1133                                 {
1134                                         bool we_have_delta = (Instance->Time(false) != Instance->Time(true));
1135                                         time_t them = atoi(params[0].c_str());
1136                                         time_t delta = them - Instance->Time(false);
1137                                         if ((delta < -600) || (delta > 600))
1138                                         {
1139                                                 Instance->SNO->WriteToSnoMask('l',"\2ERROR\2: Your clocks are out by %d seconds (this is more than five minutes). Link aborted, \2PLEASE SYNC YOUR CLOCKS!\2",abs(delta));
1140                                                 SendError("Your clocks are out by "+ConvToStr(abs(delta))+" seconds (this is more than five minutes). Link aborted, PLEASE SYNC YOUR CLOCKS!");
1141                                                 return false;
1142                                         }
1143                                         else if ((delta < -30) || (delta > 30))
1144                                         {
1145                                                 Instance->SNO->WriteToSnoMask('l',"\2WARNING\2: Your clocks are out by %d seconds. Please consider synching your clocks.", abs(delta));
1146                                         }
1147
1148                                         if (!Utils->MasterTime && !we_have_delta)
1149                                         {
1150                                                 this->Instance->SetTimeDelta(delta);
1151                                                 // Send this new timestamp to any other servers
1152                                                 Utils->DoOneToMany(Instance->Config->GetSID(), "TIMESET", params);
1153                                         }
1154                                 }
1155                                 this->LinkState = CONNECTED;
1156                                 Link* lnk = Utils->FindLink(InboundServerName);
1157
1158                                 Node = new TreeServer(this->Utils, this->Instance, InboundServerName, InboundDescription, InboundSID, Utils->TreeRoot, this, lnk ? lnk->Hidden : false);
1159
1160                                 if (Node->DuplicateID())
1161                                 {
1162                                         this->SendError("Server ID "+InboundSID+" already exists on the network!");
1163                                         this->Instance->SNO->WriteToSnoMask('l',"Server \2"+InboundServerName+"\2 being introduced from \2" + prefix + "\2 denied, server ID already exists on the network. Closing link.");
1164                                         return false;
1165                                 }
1166
1167                                 Utils->TreeRoot->AddChild(Node);
1168                                 params.clear();
1169                                 params.push_back(InboundServerName);
1170                                 params.push_back("*");
1171                                 params.push_back("1");
1172                                 params.push_back(InboundSID);
1173                                 params.push_back(":"+InboundDescription);
1174                                 Utils->DoOneToAllButSender(Instance->Config->GetSID(),"SERVER",params,InboundServerName);
1175                                 Node->bursting = true;
1176                                 this->DoBurst(Node);
1177                         }
1178                         else if (command == "ERROR")
1179                         {
1180                                 return this->Error(params);
1181                         }
1182                         else if (command == "CAPAB")
1183                         {
1184                                 return this->Capab(params);
1185                         }
1186
1187                 break;
1188                 case LISTENER:
1189                         /*
1190                          * This really shouldn't happen.
1191                          */
1192                         this->SendError("Internal error -- listening socket accepted its own descriptor!!!");
1193                         return false;
1194                 break;
1195                 case CONNECTING:
1196                         /*
1197                          * State CONNECTING:
1198                          *  We're connecting (OUTGOING) to another server. They are in state WAIT_AUTH_1 until they verify
1199                          *  our credentials, when they proceed into WAIT_AUTH_2 and send SERVER to us. We then send BURST
1200                          *  + our netburst, which will put them into CONNECTED state. -- w
1201                          */
1202                         if (command == "SERVER")
1203                         {
1204                                 // Our credentials have been accepted, send netburst. (this puts US into the CONNECTED state)
1205                                 return this->Outbound_Reply_Server(params);
1206                         }
1207                         else if (command == "ERROR")
1208                         {
1209                                 return this->Error(params);
1210                         }
1211                         else if (command == "CAPAB")
1212                         {
1213                                 return this->Capab(params);
1214                         }
1215                 break;
1216                 case CONNECTED:
1217                         /*
1218                         * State CONNECTED:
1219                          *  Credentials have been exchanged, we've gotten their 'BURST' (or sent ours).
1220                          *  Anything from here on should be accepted a little more reasonably.
1221                          */
1222                         if (!prefix.empty())
1223                         {
1224                                 /*
1225                                  * Check for fake direction here, and drop any instances that are found.
1226                                  * What is fake direction? Imagine the following server setup:
1227                                  *    0AA <-> 0AB <-> 0AC
1228                                  * Fake direction would be 0AC sending a message to 0AB claiming to be from
1229                                  * 0AA, or something similar. Basically, a message taking a path that *cannot*
1230                                  * be correct.
1231                                  *
1232                                  * When would this be seen?
1233                                  * Well, hopefully never. It could be caused by race conditions, bugs, or
1234                                  * "miscreant" servers, though, so let's check anyway. -- w
1235                                  */
1236                                 std::string direction = prefix;
1237
1238                                 User *t = this->Instance->FindUUID(prefix);
1239                                 if (t)
1240                                 {
1241                                         direction = t->server;
1242                                 }
1243
1244                                 TreeServer* route_back_again = Utils->BestRouteTo(direction);
1245                                 if ((!route_back_again) || (route_back_again->GetSocket() != this))
1246                                 {
1247                                         if (route_back_again)
1248                                                 Instance->Log(DEBUG,"Protocol violation: Fake direction in command '%s' from connection '%s'",line.c_str(),this->GetName().c_str());
1249                                         return true;
1250                                 }
1251                                 /* Fix by brain:
1252                                  * When there is activity on the socket, reset the ping counter so
1253                                  * that we're not wasting bandwidth pinging an active server.
1254                                  */
1255                                 route_back_again->SetNextPingTime(time(NULL) + Utils->PingFreq);
1256                                 route_back_again->SetPingFlag();
1257                         }
1258                         else
1259                         {
1260                                 /*
1261                                  * Empty prefix from a server to server link:
1262                                  *  This is somewhat bad/naughty, so let's set the prefix
1263                                  *  to be the link that we got it from, so we don't break anything. -- w
1264                                  */
1265                                 TreeServer* n = Utils->FindServer(GetName());
1266                                 if (n)
1267                                         prefix = n->GetID();
1268                                 else
1269                                         prefix = GetName();
1270                         }
1271
1272                         /*
1273                          * First up, check for any malformed commands (e.g. MODE without a timestamp)
1274                          * and rewrite commands where necessary (SVSMODE -> MODE for services). -- w
1275                          */
1276                         if (command == "MODE")
1277                         {
1278                                 if (params.size() >= 2)
1279                                 {
1280                                         Channel* channel = Instance->FindChan(params[0]);
1281                                         if (channel)
1282                                         {
1283                                                 User* x = Instance->FindNick(prefix);
1284                                                 if (x)
1285                                                 {
1286                                                         if (warned.find(x->server) == warned.end())
1287                                                         {
1288                                                                 Instance->Log(DEFAULT,"WARNING: I revceived modes '%s' from another server '%s'. This is not compliant with InspIRCd. Please check that server for bugs.", params[1].c_str(), x->server);
1289                                                                 Instance->SNO->WriteToSnoMask('d', "WARNING: The server %s is sending nonstandard modes: '%s MODE %s' where FMODE should be used, and may cause desyncs.", x->server, x->nick, params[1].c_str());
1290                                                                 warned[x->server] = x->nick;
1291                                                         }
1292                                                 }
1293                                         }
1294                                 }
1295                         }
1296                         else if (command == "SVSMODE")
1297                         {
1298                                 command = "MODE";
1299                         }
1300
1301
1302                         /*
1303                          * Now, check for (and parse) commands as appropriate. -- w
1304                          */     
1305                 
1306                         /* Find the server that this command originated from, used in the handlers below */
1307                         TreeServer *ServerSource = Utils->FindServer(prefix);
1308
1309                         /* Find the link we just got this from so we don't bounce it back incorrectly */
1310                         std::string sourceserv = this->myhost;
1311                         if (!this->InboundServerName.empty())
1312                         {
1313                                 sourceserv = this->InboundServerName;
1314                         }
1315
1316                         /*
1317                          * XXX one of these days, this needs to be moved into class Commands.
1318                          */
1319                         if (command == "UID")
1320                         {
1321                                 return this->ParseUID(prefix, params);
1322                         }
1323                         else if (command == "FJOIN")
1324                         {
1325                                 return this->ForceJoin(prefix,params);
1326                         }
1327                         else if (command == "STATS")
1328                         {
1329                                 return this->Stats(prefix, params);
1330                         }
1331                         else if (command == "MOTD")
1332                         {
1333                                 return this->Motd(prefix, params);
1334                         }
1335                         else if (command == "KILL" && ServerSource)
1336                         {
1337                                 // Kill from a server
1338                                 return this->RemoteKill(prefix,params);
1339                         }
1340                         else if (command == "MODULES")
1341                         {
1342                                 return this->Modules(prefix, params);
1343                         }
1344                         else if (command == "ADMIN")
1345                         {
1346                                 return this->Admin(prefix, params);
1347                         }
1348                         else if (command == "SERVER")
1349                         {
1350                                 return this->RemoteServer(prefix,params);
1351                         }
1352                         else if (command == "ERROR")
1353                         {
1354                                 return this->Error(params);
1355                         }
1356                         else if (command == "OPERTYPE")
1357                         {
1358                                 return this->OperType(prefix,params);
1359                         }
1360                         else if (command == "FMODE")
1361                         {
1362                                 return this->ForceMode(prefix,params);
1363                         }
1364                         else if (command == "FTOPIC")
1365                         {
1366                                 return this->ForceTopic(prefix,params);
1367                         }
1368                         else if (command == "REHASH")
1369                         {
1370                                 return this->RemoteRehash(prefix,params);
1371                         }
1372                         else if (command == "METADATA")
1373                         {
1374                                 return this->MetaData(prefix,params);
1375                         }
1376                         else if (command == "REMSTATUS")
1377                         {
1378                                 return this->RemoveStatus(prefix,params);
1379                         }
1380                         else if (command == "PING")
1381                         {
1382                                 return this->LocalPing(prefix,params);
1383                         }
1384                         else if (command == "PONG")
1385                         {
1386                                 return this->LocalPong(prefix,params);
1387                         }
1388                         else if (command == "VERSION")
1389                         {
1390                                 return this->ServerVersion(prefix,params);
1391                         }
1392                         else if (command == "FHOST")
1393                         {
1394                                 return this->ChangeHost(prefix,params);
1395                         }
1396                         else if (command == "FNAME")
1397                         {
1398                                 return this->ChangeName(prefix,params);
1399                         }
1400                         else if (command == "ADDLINE")
1401                         {
1402                                 return this->AddLine(prefix,params);
1403                         }
1404                         else if (command == "DELLINE")
1405                         {
1406                                 return this->DelLine(prefix,params);
1407                         }
1408                         else if (command == "SVSNICK")
1409                         {
1410                                 return this->ForceNick(prefix,params);
1411                         }
1412                         else if (command == "OPERQUIT")
1413                         {
1414                                 return this->OperQuit(prefix,params);
1415                         }
1416                         else if (command == "IDLE")
1417                         {
1418                                 return this->Whois(prefix,params);
1419                         }
1420                         else if (command == "PUSH")
1421                         {
1422                                 return this->Push(prefix,params);
1423                         }
1424                         else if (command == "TIMESET")
1425                         {
1426                                 return this->HandleSetTime(prefix, params);
1427                         }
1428                         else if (command == "TIME")
1429                         {
1430                                 return this->Time(prefix,params);
1431                         }
1432                         else if ((command == "KICK") && (Utils->IsServer(prefix)))
1433                         {
1434                                 if (params.size() == 3)
1435                                 {
1436                                         User* user = this->Instance->FindNick(params[1]);
1437                                         Channel* chan = this->Instance->FindChan(params[0]);
1438                                         if (user && chan)
1439                                         {
1440                                                 if (!chan->ServerKickUser(user, params[2].c_str(), false))
1441                                                         /* Yikes, the channels gone! */
1442                                                         delete chan;
1443                                         }
1444                                 }
1445
1446                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
1447                         }
1448                         else if (command == "SVSJOIN")
1449                         {
1450                                 return this->ServiceJoin(prefix,params);
1451                         }
1452                         else if (command == "SVSPART")
1453                         {
1454                                 return this->ServicePart(prefix,params);
1455                         }
1456                         else if (command == "SQUIT")
1457                         {
1458                                 if (params.size() == 2)
1459                                 {
1460                                         this->Squit(Utils->FindServer(params[0]),params[1]);
1461                                 }
1462                                 return true;
1463                         }
1464                         else if (command == "OPERNOTICE")
1465                         {
1466                                 if (params.size() >= 1)
1467                                         Instance->SNO->WriteToSnoMask('A', "From " + prefix + ": " + params[0]);
1468                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
1469                         }
1470                         else if (command == "MODENOTICE")
1471                         {
1472                                 if (params.size() >= 2)
1473                                 {
1474                                         Instance->Users->WriteMode(params[0].c_str(), WM_AND, "*** From %s: %s", prefix.c_str(), params[1].c_str());
1475                                 }
1476                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
1477                         }
1478                         else if (command == "SNONOTICE")
1479                         {
1480                                 if (params.size() >= 2)
1481                                 {
1482                                         Instance->SNO->WriteToSnoMask(*(params[0].c_str()), "From " + prefix + ": "+ params[1]);
1483                                 }
1484                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
1485                         }
1486                         else if (command == "BURST")
1487                         {
1488                                 // Set prefix server as bursting
1489                                 if (!ServerSource)
1490                                 {
1491                                         this->Instance->SNO->WriteToSnoMask('l', "WTF: Got BURST from a nonexistant server(?): %s", prefix.c_str());
1492                                         return false;
1493                                 }
1494                                 
1495                                 ServerSource->bursting = true;
1496                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
1497                         }
1498                         else if (command == "ENDBURST")
1499                         {
1500                                 if (!ServerSource)
1501                                 {
1502                                         this->Instance->SNO->WriteToSnoMask('l', "WTF: Got ENDBURST from a nonexistant server(?): %s", prefix.c_str());
1503                                         return false;
1504                                 }
1505                                 
1506                                 ServerSource->FinishBurst();
1507                                 return Utils->DoOneToAllButSenderRaw(line, sourceserv, prefix, command, params);
1508                         }
1509                         else if (command == "MODE")
1510                         {
1511                                 // Server-prefix MODE.
1512                                 const char* modelist[MAXPARAMETERS];
1513                                 for (size_t i = 0; i < params.size(); i++)
1514                                         modelist[i] = params[i].c_str();
1515                                         
1516                                 // Insert into the parser
1517                                 this->Instance->SendMode(modelist, params.size(), this->Instance->FakeClient);
1518                                 
1519                                 // Pass out to the network
1520                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
1521                         }
1522                         else
1523                         {
1524                                 /*
1525                                  * Not a special s2s command. Emulate the user doing it.
1526                                  * This saves us having a huge ugly command parser again.
1527                                  */
1528                                 User *who = this->Instance->FindUUID(prefix);
1529
1530                                 if (!who)
1531                                 {
1532                                         // this looks ugly because command is an irc::string
1533                                         this->SendError("Command (" + std::string(command.c_str()) + ") from unknown prefix (" + prefix + ")! Dropping link.");
1534                                         return false;
1535                                 }
1536
1537                                 if (command == "NICK")
1538                                 {
1539                                         if (params.size() != 2)
1540                                         {
1541                                                 SendError("Protocol violation: NICK message without TS - :"+std::string(who->uuid)+" NICK "+params[0]);
1542                                                 return false;
1543                                         }
1544                                         /* Update timestamp on user when they change nicks */
1545                                         who->age = atoi(params[1].c_str());
1546
1547                                         /*
1548                                          * On nick messages, check that the nick doesnt already exist here.
1549                                          * If it does, perform collision logic.
1550                                          */
1551                                         User* x = this->Instance->FindNickOnly(params[0]);
1552                                         if ((x) && (x != who))
1553                                         {
1554                                                 int collideret = 0;
1555                                                 /* x is local, who is remote */
1556                                                 collideret = this->DoCollision(x, who->age, who->ident, who->GetIPString(), who->uuid);
1557                                                 if (collideret != 1)
1558                                                 {
1559                                                         /*
1560                                                          * Remote client lost, or both lost, parsing this nickchange would be
1561                                                          * pointless, as the incoming client's server will soon recieve SVSNICK to
1562                                                          * change its nick to its UID. :) -- w00t
1563                                                          */
1564                                                         return true;
1565                                                 }
1566                                         }
1567                                 }
1568                                         
1569                                 // its a user
1570                                 const char* strparams[127];
1571                                 for (unsigned int q = 0; q < params.size(); q++)
1572                                 {
1573                                         strparams[q] = params[q].c_str();
1574                                 }
1575
1576                                 switch (this->Instance->CallCommandHandler(command.c_str(), strparams, params.size(), who))
1577                                 {
1578                                         case CMD_INVALID:
1579                                                 // command is irc::string, hence ugliness
1580                                                 this->SendError("Unrecognised or malformed command '" + std::string(command.c_str()) + "' -- possibly loaded mismatched modules");
1581                                                 return false;
1582                                                 break;
1583                                         /*
1584                                          * CMD_LOCALONLY is aliased to CMD_FAILURE, so this won't go out onto the network.
1585                                          */
1586                                         case CMD_FAILURE:
1587                                                 return true;
1588                                                 break;
1589                                         default:
1590                                                 /* CMD_SUCCESS and CMD_USER_DELETED fall through here */
1591                                                 break;
1592                                 }
1593
1594                                 return Utils->DoOneToAllButSenderRaw(line,sourceserv,prefix,command,params);
1595
1596                         }
1597                         return true;
1598                         break; // end of state CONNECTED (phew).
1599         }
1600         return true;
1601 }
1602
1603 std::string TreeSocket::GetName()
1604 {
1605         std::string sourceserv = this->myhost;
1606         if (!this->InboundServerName.empty())
1607         {
1608                 sourceserv = this->InboundServerName;
1609         }
1610         return sourceserv;
1611 }
1612
1613 void TreeSocket::OnTimeout()
1614 {
1615         if (this->LinkState == CONNECTING)
1616         {
1617                 Utils->Creator->RemoteMessage(NULL, "CONNECT: Connection to \002%s\002 timed out.", myhost.c_str());
1618                 Link* MyLink = Utils->FindLink(myhost);
1619                 if (MyLink)
1620                         Utils->DoFailOver(MyLink);
1621         }
1622 }
1623
1624 void TreeSocket::OnClose()
1625 {
1626         // Test fix for big fuckup
1627         if (this->LinkState != CONNECTED)
1628                 return;
1629
1630         // Connection closed.
1631         // If the connection is fully up (state CONNECTED)
1632         // then propogate a netsplit to all peers.
1633         std::string quitserver = this->myhost;
1634         if (!this->InboundServerName.empty())
1635         {
1636                 quitserver = this->InboundServerName;
1637         }
1638         TreeServer* s = Utils->FindServer(quitserver);
1639         if (s)
1640         {
1641                 Squit(s,"Remote host closed the connection");
1642         }
1643
1644         if (!quitserver.empty())
1645         {
1646                 Utils->Creator->RemoteMessage(NULL,"Connection to '\2%s\2' failed.",quitserver.c_str());
1647                 time_t server_uptime = Instance->Time() - this->age;    
1648                 if (server_uptime)
1649                         Utils->Creator->RemoteMessage(NULL,"Connection to '\2%s\2' was established for %s", quitserver.c_str(), Utils->Creator->TimeToStr(server_uptime).c_str());
1650         }
1651 }
1652
1653 int TreeSocket::OnIncomingConnection(int newsock, char* ip)
1654 {
1655         /* To prevent anyone from attempting to flood opers/DDoS by connecting to the server port,
1656          * or discovering if this port is the server port, we don't allow connections from any
1657          * IPs for which we don't have a link block.
1658          */
1659         bool found = false;
1660
1661         found = (std::find(Utils->ValidIPs.begin(), Utils->ValidIPs.end(), ip) != Utils->ValidIPs.end());
1662         if (!found)
1663         {
1664                 for (vector<std::string>::iterator i = Utils->ValidIPs.begin(); i != Utils->ValidIPs.end(); i++)
1665                         if (irc::sockets::MatchCIDR(ip, (*i).c_str()))
1666                                 found = true;
1667
1668                 if (!found)
1669                 {
1670                         Utils->Creator->RemoteMessage(NULL,"Server connection from %s denied (no link blocks with that IP address)", ip);
1671                         Instance->SE->Close(newsock);
1672                         return false;
1673                 }
1674         }
1675
1676         TreeSocket* s = new TreeSocket(this->Utils, this->Instance, newsock, ip, this->Hook);
1677         s = s; /* Whinge whinge whinge, thats all GCC ever does. */
1678         return true;
1679 }