]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_spanningtree/treesocket1.cpp
Fix trivial desync with FTOPIC: if topic is identical, we still need to broadcast...
[user/henk/code/inspircd.git] / src / modules / m_spanningtree / treesocket1.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 "m_hash.h"
22 #include "socketengine.h"
23
24 #include "m_spanningtree/main.h"
25 #include "m_spanningtree/utils.h"
26 #include "m_spanningtree/treeserver.h"
27 #include "m_spanningtree/link.h"
28 #include "m_spanningtree/treesocket.h"
29 #include "m_spanningtree/resolvers.h"
30 #include "m_spanningtree/handshaketimer.h"
31
32 /* $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 m_hash.h */
33
34
35 /** Because most of the I/O gubbins are encapsulated within
36  * BufferedSocket, we just call the superclass constructor for
37  * most of the action, and append a few of our own values
38  * to it.
39  */
40 TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, Module* HookMod)
41         : BufferedSocket(SI, host, port, listening, maxtime), Utils(Util), Hook(HookMod)
42 {
43         myhost = host;
44         this->LinkState = LISTENER;
45         theirchallenge.clear();
46         ourchallenge.clear();
47         if (listening && Hook)
48                 BufferedSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
49 }
50
51 TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, std::string host, int port, bool listening, unsigned long maxtime, const std::string &ServerName, const std::string &bindto, Module* HookMod)
52         : BufferedSocket(SI, host, port, listening, maxtime, bindto), Utils(Util), Hook(HookMod)
53 {
54         myhost = ServerName;
55         theirchallenge.clear();
56         ourchallenge.clear();
57         this->LinkState = CONNECTING;
58         if (Hook)
59                 BufferedSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
60 }
61
62 /** When a listening socket gives us a new file descriptor,
63  * we must associate it with a socket without creating a new
64  * connection. This constructor is used for this purpose.
65  */
66 TreeSocket::TreeSocket(SpanningTreeUtilities* Util, InspIRCd* SI, int newfd, char* ip, Module* HookMod)
67         : BufferedSocket(SI, newfd, ip), Utils(Util), Hook(HookMod)
68 {
69         this->LinkState = WAIT_AUTH_1;
70         theirchallenge.clear();
71         ourchallenge.clear();
72         sentcapab = false;
73         /* If we have a transport module hooked to the parent, hook the same module to this
74          * socket, and set a timer waiting for handshake before we send CAPAB etc.
75          */
76         if (Hook)
77                 BufferedSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
78
79         Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(Utils->LinkBlocks[0]), this->Utils, 1));
80 }
81
82 ServerState TreeSocket::GetLinkState()
83 {
84         return this->LinkState;
85 }
86
87 Module* TreeSocket::GetHook()
88 {
89         return this->Hook;
90 }
91
92 TreeSocket::~TreeSocket()
93 {
94         if (Hook)
95                 BufferedSocketUnhookRequest(this, (Module*)Utils->Creator, Hook).Send();
96 }
97
98 const std::string& TreeSocket::GetOurChallenge()
99 {
100         return this->ourchallenge;
101 }
102
103 void TreeSocket::SetOurChallenge(const std::string &c)
104 {
105         this->ourchallenge = c;
106 }
107
108 const std::string& TreeSocket::GetTheirChallenge()
109 {
110         return this->theirchallenge;
111 }
112
113 void TreeSocket::SetTheirChallenge(const std::string &c)
114 {
115         this->theirchallenge = c;
116 }
117
118 std::string TreeSocket::MakePass(const std::string &password, const std::string &challenge)
119 {
120         /* This is a simple (maybe a bit hacky?) HMAC algorithm, thanks to jilles for
121          * suggesting the use of HMAC to secure the password against various attacks.
122          *
123          * Note: If m_sha256.so is not loaded, we MUST fall back to plaintext with no
124          *       HMAC challenge/response.
125          */
126         Module* sha256 = Instance->Modules->Find("m_sha256.so");
127         if (Utils->ChallengeResponse && sha256 && !challenge.empty())
128         {
129                 /* XXX: This is how HMAC is supposed to be done:
130                  *
131                  * sha256( (pass xor 0x5c) + sha256((pass xor 0x36) + m) )
132                  *
133                  * Note that we are encoding the hex hash, not the binary
134                  * output of the hash which is slightly different to standard.
135                  *
136                  * Don't ask me why its always 0x5c and 0x36... it just is.
137                  */
138                 std::string hmac1, hmac2;
139
140                 for (size_t n = 0; n < password.length(); n++)
141                 {
142                         hmac1 += static_cast<char>(password[n] ^ 0x5C);
143                         hmac2 += static_cast<char>(password[n] ^ 0x36);
144                 }
145
146                 hmac2 += challenge;
147                 HashResetRequest(Utils->Creator, sha256).Send();
148                 hmac2 = HashSumRequest(Utils->Creator, sha256, hmac2).Send();
149
150                 HashResetRequest(Utils->Creator, sha256).Send();
151                 std::string hmac = hmac1 + hmac2;
152                 hmac = HashSumRequest(Utils->Creator, sha256, hmac).Send();
153
154                 return "HMAC-SHA256:"+ hmac;
155         }
156         else if (!challenge.empty() && !sha256)
157                 Instance->Log(DEFAULT,"Not authenticating to server using SHA256/HMAC because we don't have m_sha256 loaded!");
158
159         return password;
160 }
161
162 /** When an outbound connection finishes connecting, we receive
163  * this event, and must send our SERVER string to the other
164  * side. If the other side is happy, as outlined in the server
165  * to server docs on the inspircd.org site, the other side
166  * will then send back its own server string.
167  */
168 bool TreeSocket::OnConnected()
169 {
170         if (this->LinkState == CONNECTING)
171         {
172                 /* we do not need to change state here. */
173                 for (std::vector<Link>::iterator x = Utils->LinkBlocks.begin(); x < Utils->LinkBlocks.end(); x++)
174                 {
175                         if (x->Name == this->myhost)
176                         {
177                                 Utils->Creator->RemoteMessage(NULL,"Connection to \2%s\2[%s] started.", myhost.c_str(), (x->HiddenFromStats ? "<hidden>" : this->GetIP().c_str()));
178                                 if (Hook)
179                                 {
180                                         BufferedSocketHookRequest(this, (Module*)Utils->Creator, Hook).Send();
181                                         Utils->Creator->RemoteMessage(NULL,"Connection to \2%s\2[%s] using transport \2%s\2", myhost.c_str(), (x->HiddenFromStats ? "<hidden>" : this->GetIP().c_str()),
182                                                         x->Hook.c_str());
183                                 }
184                                 this->OutboundPass = x->SendPass;
185                                 sentcapab = false;
186
187                                 /* found who we're supposed to be connecting to, send the neccessary gubbins. */
188                                 if (this->GetHook())
189                                         Instance->Timers->AddTimer(new HandshakeTimer(Instance, this, &(*x), this->Utils, 1));
190                                 else
191                                         this->SendCapabilities();
192
193                                 return true;
194                         }
195                 }
196         }
197         /* There is a (remote) chance that between the /CONNECT and the connection
198          * being accepted, some muppet has removed the <link> block and rehashed.
199          * If that happens the connection hangs here until it's closed. Unlikely
200          * and rather harmless.
201          */
202         this->Utils->Creator->RemoteMessage(NULL,"Connection to \2%s\2 lost link tag(!)", myhost.c_str());
203         return true;
204 }
205
206 void TreeSocket::OnError(BufferedSocketError e)
207 {
208         Link* MyLink;
209
210         if (this->LinkState == LISTENER)
211                 return;
212
213         switch (e)
214         {
215                 case I_ERR_CONNECT:
216                         Utils->Creator->RemoteMessage(NULL,"Connection failed: Connection to \002%s\002 refused", myhost.c_str());
217                         MyLink = Utils->FindLink(myhost);
218                         if (MyLink)
219                                 Utils->DoFailOver(MyLink);
220                 break;
221                 case I_ERR_SOCKET:
222                         Utils->Creator->RemoteMessage(NULL,"Connection failed: Could not create socket");
223                 break;
224                 case I_ERR_BIND:
225                         Utils->Creator->RemoteMessage(NULL,"Connection failed: Error binding socket to address or port");
226                 break;
227                 case I_ERR_WRITE:
228                         Utils->Creator->RemoteMessage(NULL,"Connection failed: I/O error on connection");
229                 break;
230                 case I_ERR_NOMOREFDS:
231                         Utils->Creator->RemoteMessage(NULL,"Connection failed: Operating system is out of file descriptors!");
232                 break;
233                 default:
234                         if ((errno) && (errno != EINPROGRESS) && (errno != EAGAIN))
235                                 Utils->Creator->RemoteMessage(NULL,"Connection to \002%s\002 failed with OS error: %s", myhost.c_str(), strerror(errno));
236                 break;
237         }
238 }
239
240 int TreeSocket::OnDisconnect()
241 {
242         /* For the same reason as above, we don't
243          * handle OnDisconnect()
244          */
245         return true;
246 }
247
248 /** Recursively send the server tree with distances as hops.
249  * This is used during network burst to inform the other server
250  * (and any of ITS servers too) of what servers we know about.
251  * If at any point any of these servers already exist on the other
252  * end, our connection may be terminated. The hopcounts given
253  * by this function are relative, this doesn't matter so long as
254  * they are all >1, as all the remote servers re-calculate them
255  * to be relative too, with themselves as hop 0.
256  */
257 void TreeSocket::SendServers(TreeServer* Current, TreeServer* s, int hops)
258 {
259         char command[1024];
260         for (unsigned int q = 0; q < Current->ChildCount(); q++)
261         {
262                 TreeServer* recursive_server = Current->GetChild(q);
263                 if (recursive_server != s)
264                 {
265                         snprintf(command,1024,":%s SERVER %s * %d %s :%s",Current->GetName().c_str(),recursive_server->GetName().c_str(),hops,
266                                         recursive_server->GetID().c_str(),
267                                         recursive_server->GetDesc().c_str());
268                         this->WriteLine(command);
269                         this->WriteLine(":"+recursive_server->GetName()+" VERSION :"+recursive_server->GetVersion());
270                         /* down to next level */
271                         this->SendServers(recursive_server, s, hops+1);
272                 }
273         }
274 }
275
276 std::string TreeSocket::RandString(unsigned int length)
277 {
278         char* randombuf = new char[length+1];
279         std::string out;
280 #ifdef WINDOWS
281         int fd = -1;
282 #else
283         int fd = open("/dev/urandom", O_RDONLY, 0);
284 #endif
285
286         if (fd >= 0)
287         {
288 #ifndef WINDOWS
289                 read(fd, randombuf, length);
290                 close(fd);
291 #endif
292         }
293         else
294         {
295                 for (unsigned int i = 0; i < length; i++)
296                         randombuf[i] = rand();
297         }
298
299         for (unsigned int i = 0; i < length; i++)
300         {
301                 char randchar = static_cast<char>((randombuf[i] & 0x7F) | 0x21);
302                 out += (randchar == '=' ? '_' : randchar);
303         }
304
305         delete[] randombuf;
306         return out;
307 }
308
309 void TreeSocket::SendError(const std::string &errormessage)
310 {
311         /* Display the error locally as well as sending it remotely */
312         Utils->Creator->RemoteMessage(NULL, "Sent \2ERROR\2 to %s: %s", (this->InboundServerName.empty() ? "<unknown>" : this->InboundServerName.c_str()), errormessage.c_str());
313         this->WriteLine("ERROR :"+errormessage);
314         /* One last attempt to make sure the error reaches its target */
315         this->FlushWriteBuffer();
316 }
317
318 /** This function forces this server to quit, removing this server
319  * and any users on it (and servers and users below that, etc etc).
320  * It's very slow and pretty clunky, but luckily unless your network
321  * is having a REAL bad hair day, this function shouldnt be called
322  * too many times a month ;-)
323  */
324 void TreeSocket::SquitServer(std::string &from, TreeServer* Current)
325 {
326         /* recursively squit the servers attached to 'Current'.
327          * We're going backwards so we don't remove users
328          * while we still need them ;)
329          */
330         for (unsigned int q = 0; q < Current->ChildCount(); q++)
331         {
332                 TreeServer* recursive_server = Current->GetChild(q);
333                 this->SquitServer(from,recursive_server);
334         }
335         /* Now we've whacked the kids, whack self */
336         num_lost_servers++;
337         num_lost_users += Current->QuitUsers(from);
338 }
339
340 /** This is a wrapper function for SquitServer above, which
341  * does some validation first and passes on the SQUIT to all
342  * other remaining servers.
343  */
344 void TreeSocket::Squit(TreeServer* Current, const std::string &reason)
345 {
346         if ((Current) && (Current != Utils->TreeRoot))
347         {
348                 Event rmode((char*)Current->GetName().c_str(), (Module*)Utils->Creator, "lost_server");
349                 rmode.Send(Instance);
350
351                 std::deque<std::string> params;
352                 params.push_back(Current->GetName());
353                 params.push_back(":"+reason);
354                 Utils->DoOneToAllButSender(Current->GetParent()->GetName(),"SQUIT",params,Current->GetName());
355                 if (Current->GetParent() == Utils->TreeRoot)
356                 {
357                         this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split: "+reason);
358                 }
359                 else
360                 {
361                         this->Instance->SNO->WriteToSnoMask('l',"Server \002"+Current->GetName()+"\002 split from server \002"+Current->GetParent()->GetName()+"\002 with reason: "+reason);
362                 }
363                 num_lost_servers = 0;
364                 num_lost_users = 0;
365                 std::string from = Current->GetParent()->GetName()+" "+Current->GetName();
366                 SquitServer(from, Current);
367                 Current->Tidy();
368                 Current->GetParent()->DelChild(Current);
369                 delete Current;
370                 this->Instance->SNO->WriteToSnoMask('l',"Netsplit complete, lost \002%d\002 users on \002%d\002 servers.", num_lost_users, num_lost_servers);
371         }
372         else
373                 Instance->Log(DEFAULT,"Squit from unknown server");
374 }
375
376 /** Send one or more FJOINs for a channel of users.
377  * If the length of a single line is more than 480-NICKMAX
378  * in length, it is split over multiple lines.
379  */
380 void TreeSocket::SendFJoins(TreeServer* Current, Channel* c)
381 {
382         std::string buffer;
383         char list[MAXBUF];
384         std::string individual_halfops = std::string(":")+this->Instance->Config->GetSID()+" FMODE "+c->name+" "+ConvToStr(c->age);
385
386         size_t dlen, curlen;
387         dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->GetSID().c_str(),c->name,(unsigned long)c->age);
388         int numusers = 1;
389         char* ptr = list + dlen;
390
391         CUList *ulist = c->GetUsers();
392         std::string modes;
393         std::string params;
394
395         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
396         {
397                 // The first parameter gets a : before it
398                 size_t ptrlen = snprintf(ptr, MAXBUF, " %s%s,%s", !numusers ? ":" : "", c->GetAllPrefixChars(i->first), i->first->uuid);
399
400                 curlen += ptrlen;
401                 ptr += ptrlen;
402
403                 numusers++;
404
405                 if (curlen > (480-NICKMAX))
406                 {
407                         buffer.append(list).append("\r\n");
408                         dlen = curlen = snprintf(list,MAXBUF,":%s FJOIN %s %lu",this->Instance->Config->GetSID().c_str(),c->name,(unsigned long)c->age);
409                         ptr = list + dlen;
410                         ptrlen = 0;
411                         numusers = 0;
412                 }
413         }
414
415         if (numusers)
416                 buffer.append(list).append("\r\n");
417
418         buffer.append(":").append(this->Instance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(c->ChanModes(true)).append("\r\n");
419
420         int linesize = 1;
421         for (BanList::iterator b = c->bans.begin(); b != c->bans.end(); b++)
422         {
423                 int size = strlen(b->data) + 2;
424                 int currsize = linesize + size;
425                 if (currsize <= 350)
426                 {
427                         modes.append("b");
428                         params.append(" ").append(b->data);
429                         linesize += size; 
430                 }
431                 if ((params.length() >= MAXMODES) || (currsize > 350))
432                 {
433                         /* Wrap at MAXMODES */
434                         buffer.append(":").append(this->Instance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params).append("\r\n");
435                         modes.clear();
436                         params.clear();
437                         linesize = 1;
438                 }
439         }
440
441         /* Only send these if there are any */
442         if (!modes.empty())
443                 buffer.append(":").append(this->Instance->Config->GetSID()).append(" FMODE ").append(c->name).append(" ").append(ConvToStr(c->age)).append(" +").append(modes).append(params);
444
445         this->WriteLine(buffer);
446 }
447
448 /** Send G, Q, Z and E lines */
449 void TreeSocket::SendXLines(TreeServer* Current)
450 {
451         char data[MAXBUF];
452         std::string buffer;
453         std::string n = this->Instance->Config->GetSID();
454         const char* sn = n.c_str();
455
456         std::vector<std::string> types = Instance->XLines->GetAllTypes();
457
458         for (std::vector<std::string>::iterator it = types.begin(); it != types.end(); ++it)
459         {
460                 XLineLookup* lookup = Instance->XLines->GetAll(*it);
461
462                 if (lookup)
463                 {
464                         for (LookupIter i = lookup->begin(); i != lookup->end(); ++i)
465                         {
466                                 snprintf(data,MAXBUF,":%s ADDLINE %s %s %s %lu %lu :%s\r\n",sn, it->c_str(), i->second->Displayable(),
467                                                 i->second->source,
468                                                 (unsigned long)i->second->set_time,
469                                                 (unsigned long)i->second->duration,
470                                                 i->second->reason);
471                                 buffer.append(data);
472                         }
473                 }
474         }
475
476         if (!buffer.empty())
477                 this->WriteLine(buffer);
478 }
479
480 /** Send channel modes and topics */
481 void TreeSocket::SendChannelModes(TreeServer* Current)
482 {
483         char data[MAXBUF];
484         std::deque<std::string> list;
485         std::string n = this->Instance->Config->GetSID();
486         const char* sn = n.c_str();
487         Instance->Log(DEBUG,"Sending channels and modes, %d to send", this->Instance->chanlist->size());
488         for (chan_hash::iterator c = this->Instance->chanlist->begin(); c != this->Instance->chanlist->end(); c++)
489         {
490                 SendFJoins(Current, c->second);
491                 if (*c->second->topic)
492                 {
493                         snprintf(data,MAXBUF,":%s FTOPIC %s %lu %s :%s",sn,c->second->name,(unsigned long)c->second->topicset,c->second->setby,c->second->topic);
494                         this->WriteLine(data);
495                 }
496                 FOREACH_MOD_I(this->Instance,I_OnSyncChannel,OnSyncChannel(c->second,(Module*)Utils->Creator,(void*)this));
497                 list.clear();
498                 c->second->GetExtList(list);
499                 for (unsigned int j = 0; j < list.size(); j++)
500                 {
501                         FOREACH_MOD_I(this->Instance,I_OnSyncChannelMetaData,OnSyncChannelMetaData(c->second,(Module*)Utils->Creator,(void*)this,list[j]));
502                 }
503         }
504 }
505
506 /** send all users and their oper state/modes */
507 void TreeSocket::SendUsers(TreeServer* Current)
508 {
509         char data[MAXBUF];
510         std::deque<std::string> list;
511         std::string dataline;
512         for (user_hash::iterator u = this->Instance->Users->clientlist->begin(); u != this->Instance->Users->clientlist->end(); u++)
513         {
514                 if (u->second->registered == REG_ALL)
515                 {
516                         TreeServer* theirserver = Utils->FindServer(u->second->server);
517                         if (theirserver)
518                         {
519                                 snprintf(data,MAXBUF,":%s UID %s %lu %s %s %s %s +%s %s %lu :%s", theirserver->GetID().c_str(), u->second->uuid,
520                                                 (unsigned long)u->second->age, u->second->nick, u->second->host, u->second->dhost,
521                                                 u->second->ident, u->second->FormatModes(), u->second->GetIPString(),
522                                                 (unsigned long)u->second->signon, u->second->fullname);
523                                 this->WriteLine(data);
524                                 if (*u->second->oper)
525                                 {
526                                         snprintf(data,MAXBUF,":%s OPERTYPE %s", u->second->uuid, u->second->oper);
527                                         this->WriteLine(data);
528                                 }
529                                 if (*u->second->awaymsg)
530                                 {
531                                         snprintf(data,MAXBUF,":%s AWAY :%s", u->second->uuid, u->second->awaymsg);
532                                         this->WriteLine(data);
533                                 }
534                         }
535
536                         FOREACH_MOD_I(this->Instance,I_OnSyncUser,OnSyncUser(u->second,(Module*)Utils->Creator,(void*)this));
537                         list.clear();
538                         u->second->GetExtList(list);
539                         for (unsigned int j = 0; j < list.size(); j++)
540                         {
541                                 FOREACH_MOD_I(this->Instance,I_OnSyncUserMetaData,OnSyncUserMetaData(u->second,(Module*)Utils->Creator,(void*)this,list[j]));
542                         }
543                 }
544         }
545 }
546
547 /** This function is called when we want to send a netburst to a local
548  * server. There is a set order we must do this, because for example
549  * users require their servers to exist, and channels require their
550  * users to exist. You get the idea.
551  */
552 void TreeSocket::DoBurst(TreeServer* s)
553 {
554         std::string name = s->GetName();
555         std::string burst = ":" + this->Instance->Config->GetSID() + " BURST " +ConvToStr(Instance->Time(true));
556         std::string endburst = ":" + this->Instance->Config->GetSID() + " ENDBURST";
557         this->Instance->SNO->WriteToSnoMask('l',"Bursting to \2%s\2 (Authentication: %s).", name.c_str(), this->GetTheirChallenge().empty() ? "plaintext password" : "SHA256-HMAC challenge-response");
558         this->WriteLine(burst);
559         /* send our version string */
560         this->WriteLine(std::string(":")+this->Instance->Config->GetSID()+" VERSION :"+this->Instance->GetVersionString());
561         /* Send server tree */
562         this->SendServers(Utils->TreeRoot,s,1);
563         /* Send users and their oper status */
564         this->SendUsers(s);
565         /* Send everything else (channel modes, xlines etc) */
566         this->SendChannelModes(s);
567         this->SendXLines(s);
568         FOREACH_MOD_I(this->Instance,I_OnSyncOtherMetaData,OnSyncOtherMetaData((Module*)Utils->Creator,(void*)this));
569         this->WriteLine(endburst);
570         this->Instance->SNO->WriteToSnoMask('l',"Finished bursting to \2"+name+"\2.");
571 }
572
573 /** This function is called when we receive data from a remote
574  * server. We buffer the data in a std::string (it doesnt stay
575  * there for long), reading using BufferedSocket::Read() which can
576  * read up to 16 kilobytes in one operation.
577  *
578  * IF THIS FUNCTION RETURNS FALSE, THE CORE CLOSES AND DELETES
579  * THE SOCKET OBJECT FOR US.
580  */
581 bool TreeSocket::OnDataReady()
582 {
583         char* data = this->Read();
584         /* Check that the data read is a valid pointer and it has some content */
585         if (data && *data)
586         {
587                 this->in_buffer.append(data);
588                 /* While there is at least one new line in the buffer,
589                  * do something useful (we hope!) with it.
590                  */
591                 while (in_buffer.find("\n") != std::string::npos)
592                 {
593                         std::string ret = in_buffer.substr(0,in_buffer.find("\n")-1);
594                         in_buffer = in_buffer.substr(in_buffer.find("\n")+1,in_buffer.length()-in_buffer.find("\n"));
595                         /* Use rfind here not find, as theres more
596                          * chance of the \r being near the end of the
597                          * string, not the start.
598                          */
599                         if (ret.find("\r") != std::string::npos)
600                                 ret = in_buffer.substr(0,in_buffer.find("\r")-1);
601                         /* Process this one, abort if it
602                          * didnt return true.
603                          */
604                         if (!this->ProcessLine(ret))
605                         {
606                                 return false;
607                         }
608                 }
609                 return true;
610         }
611         /* EAGAIN returns an empty but non-NULL string, so this
612          * evaluates to TRUE for EAGAIN but to FALSE for EOF.
613          */
614         return (data && !*data);
615 }