]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/users.cpp
More pedantic safety
[user/henk/code/inspircd.git] / src / users.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 /* $Core: libIRCDusers */
15
16 #include "inspircd.h"
17 #include <stdarg.h>
18 #include "socketengine.h"
19 #include "wildcard.h"
20 #include "xline.h"
21 #include "commands/cmd_whowas.h"
22
23 static unsigned long already_sent[MAX_DESCRIPTORS] = {0};
24
25 /* XXX: Used for speeding up WriteCommon operations */
26 unsigned long uniq_id = 0;
27
28 std::string User::ProcessNoticeMasks(const char *sm)
29 {
30         bool adding = true, oldadding = false;
31         const char *c = sm;
32         std::string output;
33
34         while (c && *c)
35         {
36                 switch (*c)
37                 {
38                         case '+':
39                                 adding = true;
40                         break;
41                         case '-':
42                                 adding = false;
43                         break;
44                         case '*':
45                                 for (unsigned char d = 'A'; d <= 'z'; d++)
46                                 {
47                                         if (ServerInstance->SNO->IsEnabled(d))
48                                         {
49                                                 if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
50                                                 {
51                                                         if ((oldadding != adding) || (!output.length()))
52                                                                 output += (adding ? '+' : '-');
53
54                                                         this->SetNoticeMask(d, adding);
55
56                                                         output += d;
57                                                 }
58                                         }
59                                         oldadding = adding;
60                                 }
61                         break;
62                         default:
63                                 if ((*c >= 'A') && (*c <= 'z') && (ServerInstance->SNO->IsEnabled(*c)))
64                                 {
65                                         if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
66                                         {
67                                                 if ((oldadding != adding) || (!output.length()))
68                                                         output += (adding ? '+' : '-');
69
70                                                 this->SetNoticeMask(*c, adding);
71
72                                                 output += *c;
73                                         }
74                                 }
75                                 oldadding = adding;
76                         break;
77                 }
78
79                 *c++;
80         }
81
82         return output;
83 }
84
85 void User::StartDNSLookup()
86 {
87         try
88         {
89                 bool cached;
90                 const char* ip = this->GetIPString();
91
92                 /* Special case for 4in6 (Have i mentioned i HATE 4in6?) */
93                 if (!strncmp(ip, "0::ffff:", 8))
94                         res_reverse = new UserResolver(this->ServerInstance, this, ip + 8, DNS_QUERY_PTR4, cached);
95                 else
96                         res_reverse = new UserResolver(this->ServerInstance, this, ip, this->GetProtocolFamily() == AF_INET ? DNS_QUERY_PTR4 : DNS_QUERY_PTR6, cached);
97
98                 this->ServerInstance->AddResolver(res_reverse, cached);
99         }
100         catch (CoreException& e)
101         {
102                 ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
103         }
104 }
105
106 bool User::IsNoticeMaskSet(unsigned char sm)
107 {
108         return (snomasks[sm-65]);
109 }
110
111 void User::SetNoticeMask(unsigned char sm, bool value)
112 {
113         snomasks[sm-65] = value;
114 }
115
116 const char* User::FormatNoticeMasks()
117 {
118         static char data[MAXBUF];
119         int offset = 0;
120
121         for (int n = 0; n < 64; n++)
122         {
123                 if (snomasks[n])
124                         data[offset++] = n+65;
125         }
126
127         data[offset] = 0;
128         return data;
129 }
130
131
132
133 bool User::IsModeSet(unsigned char m)
134 {
135         return (modes[m-65]);
136 }
137
138 void User::SetMode(unsigned char m, bool value)
139 {
140         modes[m-65] = value;
141 }
142
143 const char* User::FormatModes()
144 {
145         static char data[MAXBUF];
146         int offset = 0;
147         for (int n = 0; n < 64; n++)
148         {
149                 if (modes[n])
150                         data[offset++] = n+65;
151         }
152         data[offset] = 0;
153         return data;
154 }
155
156 void User::DecrementModes()
157 {
158         ServerInstance->Log(DEBUG,"DecrementModes()");
159         for (unsigned char n = 'A'; n <= 'z'; n++)
160         {
161                 if (modes[n-65])
162                 {
163                         ServerInstance->Log(DEBUG,"DecrementModes() found mode %c", n);
164                         ModeHandler* mh = ServerInstance->Modes->FindMode(n, MODETYPE_USER);
165                         if (mh)
166                         {
167                                 ServerInstance->Log(DEBUG,"Found handler %c and call ChangeCount", n);
168                                 mh->ChangeCount(-1);
169                         }
170                 }
171         }
172 }
173
174 User::User(InspIRCd* Instance, const std::string &uid) : ServerInstance(Instance)
175 {
176         *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = *uuid = 0;
177         server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName);
178         reset_due = ServerInstance->Time();
179         age = ServerInstance->Time(true);
180         Penalty = 0;
181         lines_in = lastping = signon = idle_lastmsg = nping = registered = 0;
182         ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0;
183         OverPenalty = ExemptFromPenalty = muted = exempt = haspassed = dns_done = false;
184         fd = -1;
185         recvq.clear();
186         sendq.clear();
187         WriteError.clear();
188         res_forward = res_reverse = NULL;
189         Visibility = NULL;
190         ip = NULL;
191         chans.clear();
192         invites.clear();
193         memset(modes,0,sizeof(modes));
194         memset(snomasks,0,sizeof(snomasks));
195         /* Invalidate cache */
196         operquit = cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;
197
198         if (uid.empty())
199                 strlcpy(uuid, Instance->GetUID().c_str(), UUID_LENGTH);
200         else
201                 strlcpy(uuid, uid.c_str(), UUID_LENGTH);
202
203         ServerInstance->Log(DEBUG,"New UUID for user: %s (%s)", uuid, uid.empty() ? "allocated new" : "used remote");
204
205         user_hash::iterator finduuid = Instance->uuidlist->find(uuid);
206         if (finduuid == Instance->uuidlist->end())
207                 (*Instance->uuidlist)[uuid] = this;
208         else
209                 throw CoreException("Duplicate UUID "+std::string(uuid)+" in User constructor");
210 }
211
212 void User::RemoveCloneCounts()
213 {
214         clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
215         if (x != ServerInstance->local_clones.end())
216         {
217                 x->second--;
218                 if (!x->second)
219                 {
220                         ServerInstance->local_clones.erase(x);
221                 }
222         }
223         
224         clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString());
225         if (y != ServerInstance->global_clones.end())
226         {
227                 y->second--;
228                 if (!y->second)
229                 {
230                         ServerInstance->global_clones.erase(y);
231                 }
232         }
233 }
234
235 User::~User()
236 {
237         this->InvalidateCache();
238         this->DecrementModes();
239         if (operquit)
240                 free(operquit);
241         if (ip)
242         {
243                 this->RemoveCloneCounts();
244
245                 if (this->GetProtocolFamily() == AF_INET)
246                 {
247                         delete (sockaddr_in*)ip;
248                 }
249 #ifdef SUPPORT_IP6LINKS
250                 else
251                 {
252                         delete (sockaddr_in6*)ip;
253                 }
254 #endif
255         }
256
257         ServerInstance->uuidlist->erase(uuid);
258 }
259
260 char* User::MakeHost()
261 {
262         if (this->cached_makehost)
263                 return this->cached_makehost;
264
265         char nhost[MAXBUF];
266         /* This is much faster than snprintf */
267         char* t = nhost;
268         for(char* n = ident; *n; n++)
269                 *t++ = *n;
270         *t++ = '@';
271         for(char* n = host; *n; n++)
272                 *t++ = *n;
273         *t = 0;
274
275         this->cached_makehost = strdup(nhost);
276
277         return this->cached_makehost;
278 }
279
280 char* User::MakeHostIP()
281 {
282         if (this->cached_hostip)
283                 return this->cached_hostip;
284
285         char ihost[MAXBUF];
286         /* This is much faster than snprintf */
287         char* t = ihost;
288         for(char* n = ident; *n; n++)
289                 *t++ = *n;
290         *t++ = '@';
291         for(const char* n = this->GetIPString(); *n; n++)
292                 *t++ = *n;
293         *t = 0;
294
295         this->cached_hostip = strdup(ihost);
296
297         return this->cached_hostip;
298 }
299
300 void User::CloseSocket()
301 {
302         ServerInstance->SE->Shutdown(this, 2);
303         ServerInstance->SE->Close(this);
304 }
305
306 char* User::GetFullHost()
307 {
308         if (this->cached_fullhost)
309                 return this->cached_fullhost;
310
311         char result[MAXBUF];
312         char* t = result;
313         for(char* n = nick; *n; n++)
314                 *t++ = *n;
315         *t++ = '!';
316         for(char* n = ident; *n; n++)
317                 *t++ = *n;
318         *t++ = '@';
319         for(char* n = dhost; *n; n++)
320                 *t++ = *n;
321         *t = 0;
322
323         this->cached_fullhost = strdup(result);
324
325         return this->cached_fullhost;
326 }
327
328 char* User::MakeWildHost()
329 {
330         static char nresult[MAXBUF];
331         char* t = nresult;
332         *t++ = '*';     *t++ = '!';
333         *t++ = '*';     *t++ = '@';
334         for(char* n = dhost; *n; n++)
335                 *t++ = *n;
336         *t = 0;
337         return nresult;
338 }
339
340 int User::ReadData(void* buffer, size_t size)
341 {
342         if (IS_LOCAL(this))
343         {
344 #ifndef WIN32
345                 return read(this->fd, buffer, size);
346 #else
347                 return recv(this->fd, (char*)buffer, size, 0);
348 #endif
349         }
350         else
351                 return 0;
352 }
353
354
355 char* User::GetFullRealHost()
356 {
357         if (this->cached_fullrealhost)
358                 return this->cached_fullrealhost;
359
360         char fresult[MAXBUF];
361         char* t = fresult;
362         for(char* n = nick; *n; n++)
363                 *t++ = *n;
364         *t++ = '!';
365         for(char* n = ident; *n; n++)
366                 *t++ = *n;
367         *t++ = '@';
368         for(char* n = host; *n; n++)
369                 *t++ = *n;
370         *t = 0;
371
372         this->cached_fullrealhost = strdup(fresult);
373
374         return this->cached_fullrealhost;
375 }
376
377 bool User::IsInvited(const irc::string &channel)
378 {
379         for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
380         {
381                 if (channel == *i)
382                 {
383                         return true;
384                 }
385         }
386         return false;
387 }
388
389 InvitedList* User::GetInviteList()
390 {
391         return &invites;
392 }
393
394 void User::InviteTo(const irc::string &channel)
395 {
396         invites.push_back(channel);
397 }
398
399 void User::RemoveInvite(const irc::string &channel)
400 {
401         for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
402         {
403                 if (channel == *i)
404                 {
405                         invites.erase(i);
406                         return;
407                 }
408         }
409 }
410
411 bool User::HasPermission(const std::string &command)
412 {
413         char* mycmd;
414         char* savept;
415         char* savept2;
416
417         /*
418          * users on remote servers can completely bypass all permissions based checks.
419          * This prevents desyncs when one server has different type/class tags to another.
420          * That having been said, this does open things up to the possibility of source changes
421          * allowing remote kills, etc - but if they have access to the src, they most likely have
422          * access to the conf - so it's an end to a means either way.
423          */
424         if (!IS_LOCAL(this))
425                 return true;
426
427         // are they even an oper at all?
428         if (!IS_OPER(this))
429         {
430                 return false;
431         }
432
433         // check their opertype exists (!). This won't affect local users, of course.
434         opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper);
435         if (iter_opertype == ServerInstance->Config->opertypes.end())
436         {
437                 return false;
438         }
439
440         /* XXX all this strtok/strdup stuff is a bit ick and horrid -- w00t */
441         char* Classes = strdup(iter_opertype->second);
442         char* myclass = strtok_r(Classes," ",&savept);
443         while (myclass)
444         {
445                 operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass);
446                 if (iter_operclass != ServerInstance->Config->operclass.end())
447                 {
448                         char* CommandList = strdup(iter_operclass->second);
449                         mycmd = strtok_r(CommandList," ",&savept2);
450                         while (mycmd)
451                         {
452                                 if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))
453                                 {
454                                         free(Classes);
455                                         free(CommandList);
456                                         return true;
457                                 }
458                                 mycmd = strtok_r(NULL," ",&savept2);
459                         }
460                         free(CommandList);
461                 }
462                 myclass = strtok_r(NULL," ",&savept);
463         }
464         free(Classes);
465
466         return false;
467 }
468
469 /** NOTE: We cannot pass a const reference to this method.
470  * The string is changed by the workings of the method,
471  * so that if we pass const ref, we end up copying it to
472  * something we can change anyway. Makes sense to just let
473  * the compiler do that copy for us.
474  */
475 bool User::AddBuffer(std::string a)
476 {
477         try
478         {
479                 std::string::size_type i = a.rfind('\r');
480
481                 while (i != std::string::npos)
482                 {
483                         a.erase(i, 1);
484                         i = a.rfind('\r');
485                 }
486
487                 if (a.length())
488                         recvq.append(a);
489
490                 if (recvq.length() > (unsigned)this->recvqmax)
491                 {
492                         this->SetWriteError("RecvQ exceeded");
493                         ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax);
494                         return false;
495                 }
496
497                 return true;
498         }
499
500         catch (...)
501         {
502                 ServerInstance->Log(DEBUG,"Exception in User::AddBuffer()");
503                 return false;
504         }
505 }
506
507 bool User::BufferIsReady()
508 {
509         return (recvq.find('\n') != std::string::npos);
510 }
511
512 void User::ClearBuffer()
513 {
514         recvq.clear();
515 }
516
517 std::string User::GetBuffer()
518 {
519         try
520         {
521                 if (recvq.empty())
522                         return "";
523
524                 /* Strip any leading \r or \n off the string.
525                  * Usually there are only one or two of these,
526                  * so its is computationally cheap to do.
527                  */
528                 std::string::iterator t = recvq.begin();
529                 while (t != recvq.end() && (*t == '\r' || *t == '\n'))
530                 {
531                         recvq.erase(t);
532                         t = recvq.begin();
533                 }
534
535                 for (std::string::iterator x = recvq.begin(); x != recvq.end(); x++)
536                 {
537                         /* Find the first complete line, return it as the
538                          * result, and leave the recvq as whats left
539                          */
540                         if (*x == '\n')
541                         {
542                                 std::string ret = std::string(recvq.begin(), x);
543                                 recvq.erase(recvq.begin(), x + 1);
544                                 return ret;
545                         }
546                 }
547                 return "";
548         }
549
550         catch (...)
551         {
552                 ServerInstance->Log(DEBUG,"Exception in User::GetBuffer()");
553                 return "";
554         }
555 }
556
557 void User::AddWriteBuf(const std::string &data)
558 {
559         if (*this->GetWriteError())
560                 return;
561
562         if (sendq.length() + data.length() > (unsigned)this->sendqmax)
563         {
564                 /*
565                  * Fix by brain - Set the error text BEFORE calling writeopers, because
566                  * if we dont it'll recursively  call here over and over again trying
567                  * to repeatedly add the text to the sendq!
568                  */
569                 this->SetWriteError("SendQ exceeded");
570                 ServerInstance->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax);
571                 return;
572         }
573
574         try
575         {
576                 if (data.length() > MAXBUF - 2) /* MAXBUF has a value of 514, to account for line terminators */
577                         sendq.append(data.substr(0,MAXBUF - 4)).append("\r\n"); /* MAXBUF-4 = 510 */
578                 else
579                         sendq.append(data);
580         }
581         catch (...)
582         {
583                 this->SetWriteError("SendQ exceeded");
584                 ServerInstance->WriteOpers("*** User %s SendQ got an exception",this->nick);
585         }
586 }
587
588 // send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it)
589 void User::FlushWriteBuf()
590 {
591         try
592         {
593                 if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError()))
594                 {
595                         sendq.clear();
596                 }
597                 if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER))
598                 {
599                         int old_sendq_length = sendq.length();
600                         int n_sent = ServerInstance->SE->Send(this, this->sendq.data(), this->sendq.length(), 0);
601
602                         if (n_sent == -1)
603                         {
604                                 if (errno == EAGAIN)
605                                 {
606                                         /* The socket buffer is full. This isnt fatal,
607                                          * try again later.
608                                          */
609                                         this->ServerInstance->SE->WantWrite(this);
610                                 }
611                                 else
612                                 {
613                                         /* Fatal error, set write error and bail
614                                          */
615                                         this->SetWriteError(errno ? strerror(errno) : "EOF from client");
616                                         return;
617                                 }
618                         }
619                         else
620                         {
621                                 /* advance the queue */
622                                 if (n_sent)
623                                         this->sendq = this->sendq.substr(n_sent);
624                                 /* update the user's stats counters */
625                                 this->bytes_out += n_sent;
626                                 this->cmds_out++;
627                                 if (n_sent != old_sendq_length)
628                                         this->ServerInstance->SE->WantWrite(this);
629                         }
630                 }
631         }
632
633         catch (...)
634         {
635                 ServerInstance->Log(DEBUG,"Exception in User::FlushWriteBuf()");
636         }
637
638         if (this->sendq.empty())
639         {
640                 FOREACH_MOD(I_OnBufferFlushed,OnBufferFlushed(this));
641         }
642 }
643
644 void User::SetWriteError(const std::string &error)
645 {
646         try
647         {
648                 // don't try to set the error twice, its already set take the first string.
649                 if (this->WriteError.empty())
650                         this->WriteError = error;
651         }
652
653         catch (...)
654         {
655                 ServerInstance->Log(DEBUG,"Exception in User::SetWriteError()");
656         }
657 }
658
659 const char* User::GetWriteError()
660 {
661         return this->WriteError.c_str();
662 }
663
664 void User::Oper(const std::string &opertype)
665 {
666         try
667         {
668                 this->modes[UM_OPERATOR] = 1;
669                 this->WriteServ("MODE %s :+o", this->nick);
670                 FOREACH_MOD(I_OnOper, OnOper(this, opertype));
671                 ServerInstance->Log(DEFAULT,"OPER: %s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str());
672                 strlcpy(this->oper, opertype.c_str(), NICKMAX - 1);
673                 ServerInstance->all_opers.push_back(this);
674                 FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype));
675         }
676
677         catch (...)
678         {
679                 ServerInstance->Log(DEBUG,"Exception in User::Oper()");
680         }
681 }
682
683 void User::UnOper()
684 {
685         try
686         {
687                 if (IS_OPER(this))
688                 {
689                         // unset their oper type (what IS_OPER checks), and remove +o
690                         *this->oper = 0;
691                         this->modes[UM_OPERATOR] = 0;
692                         
693                         // remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404
694                         ServerInstance->all_opers.remove(this);
695                 }
696         }
697
698         catch (...)
699         {
700                 ServerInstance->Log(DEBUG,"Exception in User::UnOper()");
701         }
702 }
703
704 void User::QuitUser(InspIRCd* Instance, User *user, const std::string &quitreason, const char* operreason)
705 {
706         Instance->Log(DEBUG,"QuitUser: %s '%s'", user->nick, quitreason.c_str());
707         user->Write("ERROR :Closing link (%s@%s) [%s]", user->ident, user->host, *operreason ? operreason : quitreason.c_str());
708         user->muted = true;
709         Instance->GlobalCulls.AddItem(user, quitreason.c_str(), operreason);
710 }
711
712 /* adds or updates an entry in the whowas list */
713 void User::AddToWhoWas()
714 {
715         Command* whowas_command = ServerInstance->Parser->GetHandler("WHOWAS");
716         if (whowas_command)
717         {
718                 std::deque<classbase*> params;
719                 params.push_back(this);
720                 whowas_command->HandleInternal(WHOWAS_ADD, params);
721         }
722 }
723
724 /* add a client connection to the sockets list */
725 void User::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip)
726 {
727         /* NOTE: Calling this one parameter constructor for User automatically
728          * allocates a new UUID and places it in the hash_map.
729          */
730         User* New = NULL;
731         try
732         {
733                 New = new User(Instance);
734         }
735         catch (...)
736         {
737                 Instance->Log(DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkies have been unleashed.");
738                 Instance->WriteOpers("*** WARNING *** Duplicate UUID allocated!");
739                 return;
740         }
741
742         Instance->Log(DEBUG,"New user fd: %d", socket);
743
744         int j = 0;
745
746         Instance->unregistered_count++;
747
748         char ipaddr[MAXBUF];
749 #ifdef IPV6
750         if (socketfamily == AF_INET6)
751                 inet_ntop(AF_INET6, &((const sockaddr_in6*)ip)->sin6_addr, ipaddr, sizeof(ipaddr));
752         else
753 #endif
754         inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr));
755
756         (*(Instance->clientlist))[New->uuid] = New;
757         New->SetFd(socket);
758
759         /* The users default nick is their UUID */
760         strlcpy(New->nick, New->uuid, NICKMAX - 1);
761
762         New->server = Instance->FindServerNamePtr(Instance->Config->ServerName);
763         /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */
764         strcpy(New->ident, "unknown");
765
766         New->registered = REG_NONE;
767         New->signon = Instance->Time() + Instance->Config->dns_timeout;
768         New->lastping = 1;
769
770         New->SetSockAddr(socketfamily, ipaddr, port);
771
772         /* Smarter than your average bear^H^H^H^Hset of strlcpys. */
773         for (const char* temp = New->GetIPString(); *temp && j < 64; temp++, j++)
774                 New->dhost[j] = New->host[j] = *temp;
775         New->dhost[j] = New->host[j] = 0;
776
777         Instance->AddLocalClone(New);
778         Instance->AddGlobalClone(New);
779
780         /*
781          * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.
782          * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
783          */
784         ConnectClass* i = New->GetClass();
785
786         if (!i)
787         {
788                 User::QuitUser(Instance, New, "Access denied by configuration");
789                 return;
790         }
791
792         /*
793          * Check connect class settings and initialise settings into User.
794          * This will be done again after DNS resolution. -- w00t
795          */
796         New->CheckClass();
797
798         Instance->local_users.push_back(New);
799
800         if ((Instance->local_users.size() > Instance->Config->SoftLimit) || (Instance->local_users.size() >= MAXCLIENTS))
801         {
802                 Instance->WriteOpers("*** Warning: softlimit value has been reached: %d clients", Instance->Config->SoftLimit);
803                 User::QuitUser(Instance, New,"No more connections allowed");
804                 return;
805         }
806
807         /*
808          * XXX -
809          * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
810          * its a pretty big but for the moment valid assumption:
811          * file descriptors are handed out starting at 0, and are recycled as theyre freed.
812          * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
813          * irc server at once (or the irc server otherwise initiating this many connections, files etc)
814          * which for the time being is a physical impossibility (even the largest networks dont have more
815          * than about 10,000 users on ONE server!)
816          */
817 #ifndef WINDOWS
818         if ((unsigned int)socket >= MAX_DESCRIPTORS)
819         {
820                 User::QuitUser(Instance, New, "Server is full");
821                 return;
822         }
823 #endif
824
825         New->exempt = (Instance->XLines->matches_exception(New) != NULL);
826         if (!New->exempt)
827         {
828                 ZLine* r = Instance->XLines->matches_zline(ipaddr);
829                 if (r)
830                 {
831                         char reason[MAXBUF];
832                         if (*Instance->Config->MoronBanner)
833                                 New->WriteServ("NOTICE %s :*** %s", New->nick, Instance->Config->MoronBanner);
834                         snprintf(reason,MAXBUF,"Z-Lined: %s",r->reason);
835                         User::QuitUser(Instance, New, reason);
836                         return;
837                 }
838         }
839
840         if (socket > -1)
841         {
842                 if (!Instance->SE->AddFd(New))
843                 {
844                         Instance->Log(DEBUG,"Internal error on new connection");
845                         User::QuitUser(Instance, New, "Internal error handling connection");
846                 }
847         }
848
849         /* NOTE: even if dns lookups are *off*, we still need to display this.
850          * BOPM and other stuff requires it.
851          */
852         New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
853
854         if (Instance->Config->NoUserDns)
855         {
856                 New->dns_done = true;
857         }
858         else
859         {
860                 New->StartDNSLookup();
861         }
862 }
863
864 unsigned long User::GlobalCloneCount()
865 {
866         clonemap::iterator x = ServerInstance->global_clones.find(this->GetIPString());
867         if (x != ServerInstance->global_clones.end())
868                 return x->second;
869         else
870                 return 0;
871 }
872
873 unsigned long User::LocalCloneCount()
874 {
875         clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
876         if (x != ServerInstance->local_clones.end())
877                 return x->second;
878         else
879                 return 0;
880 }
881
882 /*
883  * Check class restrictions
884  */
885 void User::CheckClass(const std::string &explicit_class)
886 {
887         ConnectClass* a = this->GetClass(explicit_class);
888
889         if ((!a) || (a->GetType() == CC_DENY))
890         {
891                 User::QuitUser(ServerInstance, this, "Unauthorised connection");
892                 return;
893         }
894         else if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal()))
895         {
896                 User::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)");
897                 ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
898                 return;
899         }
900         else if ((a->GetMaxGlobal()) && (this->GlobalCloneCount() > a->GetMaxGlobal()))
901         {
902                 User::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (global)");
903                 ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
904                 return;
905         }
906
907         this->pingmax = a->GetPingTime();
908         this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
909         this->timeout = ServerInstance->Time() + a->GetRegTimeout();
910         this->flood = a->GetFlood();
911         this->threshold = a->GetThreshold();
912         this->sendqmax = a->GetSendqMax();
913         this->recvqmax = a->GetRecvqMax();
914         this->MaxChans = a->GetMaxChans();
915 }
916
917 void User::FullConnect()
918 {
919         ServerInstance->stats->statsConnects++;
920         this->idle_lastmsg = ServerInstance->Time();
921
922         /*
923          * You may be thinking "wtf, we checked this in User::AddClient!" - and yes, we did, BUT.
924          * At the time AddClient is called, we don't have a resolved host, by here we probably do - which
925          * may put the user into a totally seperate class with different restrictions! so we *must* check again.
926          * Don't remove this! -- w00t
927          */
928         this->CheckClass();
929         
930         /* Check the password, if one is required by the user's connect class.
931          * This CANNOT be in CheckClass(), because that is called prior to PASS as well!
932          */
933         if ((!this->GetClass()->GetPass().empty()) && (!this->haspassed))
934         {
935                 User::QuitUser(ServerInstance, this, "Invalid password");
936                 return;
937         }
938         
939         if (!this->exempt)
940         {
941                 GLine* r = ServerInstance->XLines->matches_gline(this);
942
943                 if (r)
944                 {
945                         this->muted = true;
946                         char reason[MAXBUF];
947                         if (*ServerInstance->Config->MoronBanner)
948                                 this->WriteServ("NOTICE %s :*** %s", this->nick, ServerInstance->Config->MoronBanner);
949                         snprintf(reason,MAXBUF,"G-Lined: %s",r->reason);
950                         User::QuitUser(ServerInstance, this, reason);
951                         return;
952                 }
953
954                 KLine* n = ServerInstance->XLines->matches_kline(this);
955
956                 if (n)
957                 {
958                         this->muted = true;
959                         char reason[MAXBUF];
960                         if (*ServerInstance->Config->MoronBanner)
961                                 this->WriteServ("NOTICE %s :*** %s", this, ServerInstance->Config->MoronBanner);
962                         snprintf(reason,MAXBUF,"K-Lined: %s",n->reason);
963                         User::QuitUser(ServerInstance, this, reason);
964                         return;
965                 }
966         }
967
968         this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network);
969         this->WriteServ("001 %s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host);
970         this->WriteServ("002 %s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION);
971         this->WriteServ("003 %s :This server was created %s %s", this->nick, __TIME__, __DATE__);
972         this->WriteServ("004 %s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->Modes->UserModeList().c_str(), ServerInstance->Modes->ChannelModeList().c_str(), ServerInstance->Modes->ParaModeList().c_str());
973
974         ServerInstance->Config->Send005(this);
975
976         this->WriteServ("042 %s %s :your unique ID", this->nick, this->uuid);
977
978
979         this->ShowMOTD();
980
981         /* Now registered */
982         if (ServerInstance->unregistered_count)
983                 ServerInstance->unregistered_count--;
984
985         /* Trigger LUSERS output, give modules a chance too */
986         int MOD_RESULT = 0;
987         FOREACH_RESULT(I_OnPreCommand, OnPreCommand("LUSERS", NULL, 0, this, true, "LUSERS"));
988         if (!MOD_RESULT)
989                 ServerInstance->CallCommandHandler("LUSERS", NULL, 0, this);
990
991         /*
992          * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
993          * for a user that doesn't exist yet.
994          */
995         FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
996
997         this->registered = REG_ALL;
998
999         FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
1000
1001         ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d: %s!%s@%s [%s] [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString(), this->fullname);
1002 }
1003
1004 /** User::UpdateNick()
1005  * re-allocates a nick in the user_hash after they change nicknames,
1006  * returns a pointer to the new user as it may have moved
1007  */
1008 User* User::UpdateNickHash(const char* New)
1009 {
1010         try
1011         {
1012                 //user_hash::iterator newnick;
1013                 user_hash::iterator oldnick = ServerInstance->clientlist->find(this->nick);
1014
1015                 if (!strcasecmp(this->nick,New))
1016                         return oldnick->second;
1017
1018                 if (oldnick == ServerInstance->clientlist->end())
1019                         return NULL; /* doesnt exist */
1020
1021                 User* olduser = oldnick->second;
1022                 (*(ServerInstance->clientlist))[New] = olduser;
1023                 ServerInstance->clientlist->erase(oldnick);
1024                 return olduser;
1025         }
1026
1027         catch (...)
1028         {
1029                 ServerInstance->Log(DEBUG,"Exception in User::UpdateNickHash()");
1030                 return NULL;
1031         }
1032 }
1033
1034 void User::InvalidateCache()
1035 {
1036         /* Invalidate cache */
1037         if (cached_fullhost)
1038                 free(cached_fullhost);
1039         if (cached_hostip)
1040                 free(cached_hostip);
1041         if (cached_makehost)
1042                 free(cached_makehost);
1043         if (cached_fullrealhost)
1044                 free(cached_fullrealhost);
1045         cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;
1046 }
1047
1048 bool User::ForceNickChange(const char* newnick)
1049 {
1050         try
1051         {
1052                 int MOD_RESULT = 0;
1053
1054                 this->InvalidateCache();
1055
1056                 FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick));
1057
1058                 if (MOD_RESULT)
1059                 {
1060                         ServerInstance->stats->statsCollisions++;
1061                         return false;
1062                 }
1063
1064                 if (ServerInstance->XLines->matches_qline(newnick))
1065                 {
1066                         ServerInstance->stats->statsCollisions++;
1067                         return false;
1068                 }
1069
1070                 if (this->registered == REG_ALL)
1071                 {
1072                         std::deque<classbase*> dummy;
1073                         Command* nickhandler = ServerInstance->Parser->GetHandler("NICK");
1074                         if (nickhandler)
1075                         {
1076                                 nickhandler->HandleInternal(1, dummy);
1077                                 bool result = (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS);
1078                                 nickhandler->HandleInternal(0, dummy);
1079                                 return result;
1080                         }
1081                 }
1082                 return false;
1083         }
1084
1085         catch (...)
1086         {
1087                 ServerInstance->Log(DEBUG,"Exception in User::ForceNickChange()");
1088                 return false;
1089         }
1090 }
1091
1092 void User::SetSockAddr(int protocol_family, const char* ip, int port)
1093 {
1094         switch (protocol_family)
1095         {
1096 #ifdef SUPPORT_IP6LINKS
1097                 case AF_INET6:
1098                 {
1099                         sockaddr_in6* sin = new sockaddr_in6;
1100                         sin->sin6_family = AF_INET6;
1101                         sin->sin6_port = port;
1102                         inet_pton(AF_INET6, ip, &sin->sin6_addr);
1103                         this->ip = (sockaddr*)sin;
1104                 }
1105                 break;
1106 #endif
1107                 case AF_INET:
1108                 {
1109                         sockaddr_in* sin = new sockaddr_in;
1110                         sin->sin_family = AF_INET;
1111                         sin->sin_port = port;
1112                         inet_pton(AF_INET, ip, &sin->sin_addr);
1113                         this->ip = (sockaddr*)sin;
1114                 }
1115                 break;
1116                 default:
1117                         ServerInstance->Log(DEBUG,"Uh oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);
1118                 break;
1119         }
1120 }
1121
1122 int User::GetPort()
1123 {
1124         if (this->ip == NULL)
1125                 return 0;
1126
1127         switch (this->GetProtocolFamily())
1128         {
1129 #ifdef SUPPORT_IP6LINKS
1130                 case AF_INET6:
1131                 {
1132                         sockaddr_in6* sin = (sockaddr_in6*)this->ip;
1133                         return sin->sin6_port;
1134                 }
1135                 break;
1136 #endif
1137                 case AF_INET:
1138                 {
1139                         sockaddr_in* sin = (sockaddr_in*)this->ip;
1140                         return sin->sin_port;
1141                 }
1142                 break;
1143                 default:
1144                 break;
1145         }
1146         return 0;
1147 }
1148
1149 int User::GetProtocolFamily()
1150 {
1151         if (this->ip == NULL)
1152                 return 0;
1153
1154         sockaddr_in* sin = (sockaddr_in*)this->ip;
1155         return sin->sin_family;
1156 }
1157
1158 /*
1159  * XXX the duplication here is horrid..
1160  * do we really need two methods doing essentially the same thing?
1161  */
1162 const char* User::GetIPString()
1163 {
1164         static char buf[1024];
1165
1166         if (this->ip == NULL)
1167                 return "";
1168
1169         switch (this->GetProtocolFamily())
1170         {
1171 #ifdef SUPPORT_IP6LINKS
1172                 case AF_INET6:
1173                 {
1174                         static char temp[1024];
1175
1176                         sockaddr_in6* sin = (sockaddr_in6*)this->ip;
1177                         inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
1178                         /* IP addresses starting with a : on irc are a Bad Thing (tm) */
1179                         if (*buf == ':')
1180                         {
1181                                 strlcpy(&temp[1], buf, sizeof(temp) - 1);
1182                                 *temp = '0';
1183                                 return temp;
1184                         }
1185                         return buf;
1186                 }
1187                 break;
1188 #endif
1189                 case AF_INET:
1190                 {
1191                         sockaddr_in* sin = (sockaddr_in*)this->ip;
1192                         inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
1193                         return buf;
1194                 }
1195                 break;
1196                 default:
1197                 break;
1198         }
1199         return "";
1200 }
1201
1202 /** NOTE: We cannot pass a const reference to this method.
1203  * The string is changed by the workings of the method,
1204  * so that if we pass const ref, we end up copying it to
1205  * something we can change anyway. Makes sense to just let
1206  * the compiler do that copy for us.
1207  */
1208 void User::Write(std::string text)
1209 {
1210         if (!ServerInstance->SE->BoundsCheckFd(this))
1211                 return;
1212
1213         try
1214         {
1215                 /* ServerInstance->Log(DEBUG,"C[%d] O %s", this->GetFd(), text.c_str());
1216                  * WARNING: The above debug line is VERY loud, do NOT
1217                  * enable it till we have a good way of filtering it
1218                  * out of the logs (e.g. 1.2 would be good).
1219                  */
1220                 text.append("\r\n");
1221         }
1222         catch (...)
1223         {
1224                 ServerInstance->Log(DEBUG,"Exception in User::Write() std::string::append");
1225                 return;
1226         }
1227
1228         if (ServerInstance->Config->GetIOHook(this->GetPort()))
1229         {
1230                 try
1231                 {
1232                         /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to
1233                          * implement their own buffering mechanisms
1234                          */
1235                         ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length());
1236                 }
1237                 catch (CoreException& modexcept)
1238                 {
1239                         ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
1240                 }
1241         }
1242         else
1243         {
1244                 this->AddWriteBuf(text);
1245         }
1246         ServerInstance->stats->statsSent += text.length();
1247         this->ServerInstance->SE->WantWrite(this);
1248 }
1249
1250 /** Write()
1251  */
1252 void User::Write(const char *text, ...)
1253 {
1254         va_list argsPtr;
1255         char textbuffer[MAXBUF];
1256
1257         va_start(argsPtr, text);
1258         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1259         va_end(argsPtr);
1260
1261         this->Write(std::string(textbuffer));
1262 }
1263
1264 void User::WriteServ(const std::string& text)
1265 {
1266         char textbuffer[MAXBUF];
1267
1268         snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str());
1269         this->Write(std::string(textbuffer));
1270 }
1271
1272 /** WriteServ()
1273  *  Same as Write(), except `text' is prefixed with `:server.name '.
1274  */
1275 void User::WriteServ(const char* text, ...)
1276 {
1277         va_list argsPtr;
1278         char textbuffer[MAXBUF];
1279
1280         va_start(argsPtr, text);
1281         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1282         va_end(argsPtr);
1283
1284         this->WriteServ(std::string(textbuffer));
1285 }
1286
1287
1288 void User::WriteFrom(User *user, const std::string &text)
1289 {
1290         char tb[MAXBUF];
1291
1292         snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
1293
1294         this->Write(std::string(tb));
1295 }
1296
1297
1298 /* write text from an originating user to originating user */
1299
1300 void User::WriteFrom(User *user, const char* text, ...)
1301 {
1302         va_list argsPtr;
1303         char textbuffer[MAXBUF];
1304
1305         va_start(argsPtr, text);
1306         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1307         va_end(argsPtr);
1308
1309         this->WriteFrom(user, std::string(textbuffer));
1310 }
1311
1312
1313 /* write text to an destination user from a source user (e.g. user privmsg) */
1314
1315 void User::WriteTo(User *dest, const char *data, ...)
1316 {
1317         char textbuffer[MAXBUF];
1318         va_list argsPtr;
1319
1320         va_start(argsPtr, data);
1321         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
1322         va_end(argsPtr);
1323
1324         this->WriteTo(dest, std::string(textbuffer));
1325 }
1326
1327 void User::WriteTo(User *dest, const std::string &data)
1328 {
1329         dest->WriteFrom(this, data);
1330 }
1331
1332
1333 void User::WriteCommon(const char* text, ...)
1334 {
1335         char textbuffer[MAXBUF];
1336         va_list argsPtr;
1337
1338         if (this->registered != REG_ALL)
1339                 return;
1340
1341         va_start(argsPtr, text);
1342         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1343         va_end(argsPtr);
1344
1345         this->WriteCommon(std::string(textbuffer));
1346 }
1347
1348 void User::WriteCommon(const std::string &text)
1349 {
1350         try
1351         {
1352                 bool sent_to_at_least_one = false;
1353                 char tb[MAXBUF];
1354
1355                 if (this->registered != REG_ALL)
1356                         return;
1357
1358                 uniq_id++;
1359
1360                 /* We dont want to be doing this n times, just once */
1361                 snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
1362                 std::string out = tb;
1363
1364                 for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
1365                 {
1366                         CUList* ulist = v->first->GetUsers();
1367                         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1368                         {
1369                                 if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
1370                                 {
1371                                         already_sent[i->first->fd] = uniq_id;
1372                                         i->first->Write(out);
1373                                         sent_to_at_least_one = true;
1374                                 }
1375                         }
1376                 }
1377
1378                 /*
1379                  * if the user was not in any channels, no users will receive the text. Make sure the user
1380                  * receives their OWN message for WriteCommon
1381                  */
1382                 if (!sent_to_at_least_one)
1383                 {
1384                         this->Write(std::string(tb));
1385                 }
1386         }
1387
1388         catch (...)
1389         {
1390                 ServerInstance->Log(DEBUG,"Exception in User::WriteCommon()");
1391         }
1392 }
1393
1394
1395 /* write a formatted string to all users who share at least one common
1396  * channel, NOT including the source user e.g. for use in QUIT
1397  */
1398
1399 void User::WriteCommonExcept(const char* text, ...)
1400 {
1401         char textbuffer[MAXBUF];
1402         va_list argsPtr;
1403
1404         va_start(argsPtr, text);
1405         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1406         va_end(argsPtr);
1407
1408         this->WriteCommonExcept(std::string(textbuffer));
1409 }
1410
1411 void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
1412 {
1413         char tb1[MAXBUF];
1414         char tb2[MAXBUF];
1415
1416         if (this->registered != REG_ALL)
1417                 return;
1418
1419         uniq_id++;
1420         snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost(),normal_text.c_str());
1421         snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost(),oper_text.c_str());
1422         std::string out1 = tb1;
1423         std::string out2 = tb2;
1424
1425         for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
1426         {
1427                 CUList *ulist = v->first->GetUsers();
1428                 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1429                 {
1430                         if (this != i->first)
1431                         {
1432                                 if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
1433                                 {
1434                                         already_sent[i->first->fd] = uniq_id;
1435                                         i->first->Write(IS_OPER(i->first) ? out2 : out1);
1436                                 }
1437                         }
1438                 }
1439         }
1440 }
1441
1442 void User::WriteCommonExcept(const std::string &text)
1443 {
1444         char tb1[MAXBUF];
1445         std::string out1;
1446
1447         if (this->registered != REG_ALL)
1448                 return;
1449
1450         uniq_id++;
1451         snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
1452         out1 = tb1;
1453
1454         for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
1455         {
1456                 CUList *ulist = v->first->GetUsers();
1457                 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1458                 {
1459                         if (this != i->first)
1460                         {
1461                                 if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
1462                                 {
1463                                         already_sent[i->first->fd] = uniq_id;
1464                                         i->first->Write(out1);
1465                                 }
1466                         }
1467                 }
1468         }
1469
1470 }
1471
1472 void User::WriteWallOps(const std::string &text)
1473 {
1474         if (!IS_OPER(this) && IS_LOCAL(this))
1475                 return;
1476
1477         std::string wallop("WALLOPS :");
1478         wallop.append(text);
1479
1480         for (std::vector<User*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
1481         {
1482                 User* t = *i;
1483                 if (t->IsModeSet('w'))
1484                         this->WriteTo(t,wallop);
1485         }
1486 }
1487
1488 void User::WriteWallOps(const char* text, ...)
1489 {
1490         char textbuffer[MAXBUF];
1491         va_list argsPtr;
1492
1493         va_start(argsPtr, text);
1494         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1495         va_end(argsPtr);
1496
1497         this->WriteWallOps(std::string(textbuffer));
1498 }
1499
1500 /* return 0 or 1 depending if users u and u2 share one or more common channels
1501  * (used by QUIT, NICK etc which arent channel specific notices)
1502  *
1503  * The old algorithm in 1.0 for this was relatively inefficient, iterating over
1504  * the first users channels then the second users channels within the outer loop,
1505  * therefore it was a maximum of x*y iterations (upon returning 0 and checking
1506  * all possible iterations). However this new function instead checks against the
1507  * channel's userlist in the inner loop which is a std::map<User*,User*>
1508  * and saves us time as we already know what pointer value we are after.
1509  * Don't quote me on the maths as i am not a mathematician or computer scientist,
1510  * but i believe this algorithm is now x+(log y) maximum iterations instead.
1511  */
1512 bool User::SharesChannelWith(User *other)
1513 {
1514         if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
1515                 return false;
1516
1517         /* Outer loop */
1518         for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1519         {
1520                 /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
1521                  * by replacing it with a map::find which *should* be more efficient
1522                  */
1523                 if (i->first->HasUser(other))
1524                         return true;
1525         }
1526         return false;
1527 }
1528
1529 bool User::ChangeName(const char* gecos)
1530 {
1531         if (!strcmp(gecos, this->fullname))
1532                 return true;
1533
1534         if (IS_LOCAL(this))
1535         {
1536                 int MOD_RESULT = 0;
1537                 FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos));
1538                 if (MOD_RESULT)
1539                         return false;
1540                 FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
1541         }
1542         strlcpy(this->fullname,gecos,MAXGECOS+1);
1543
1544         return true;
1545 }
1546
1547 bool User::ChangeDisplayedHost(const char* host)
1548 {
1549         if (!strcmp(host, this->dhost))
1550                 return true;
1551
1552         if (IS_LOCAL(this))
1553         {
1554                 int MOD_RESULT = 0;
1555                 FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host));
1556                 if (MOD_RESULT)
1557                         return false;
1558                 FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host));
1559         }
1560         if (this->ServerInstance->Config->CycleHosts)
1561                 this->WriteCommonExcept("QUIT :Changing hosts");
1562
1563         /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
1564         strlcpy(this->dhost,host,64);
1565
1566         this->InvalidateCache();
1567
1568         if (this->ServerInstance->Config->CycleHosts)
1569         {
1570                 for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1571                 {
1572                         i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
1573                         std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
1574                         if (n.length() > 0)
1575                                 i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
1576                 }
1577         }
1578
1579         if (IS_LOCAL(this))
1580                 this->WriteServ("396 %s %s :is now your displayed host",this->nick,this->dhost);
1581
1582         return true;
1583 }
1584
1585 bool User::ChangeIdent(const char* newident)
1586 {
1587         if (!strcmp(newident, this->ident))
1588                 return true;
1589
1590         if (this->ServerInstance->Config->CycleHosts)
1591                 this->WriteCommonExcept("%s","QUIT :Changing ident");
1592
1593         strlcpy(this->ident, newident, IDENTMAX+2);
1594
1595         this->InvalidateCache();
1596
1597         if (this->ServerInstance->Config->CycleHosts)
1598         {
1599                 for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1600                 {
1601                         i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
1602                         std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
1603                         if (n.length() > 0)
1604                                 i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
1605                 }
1606         }
1607
1608         return true;
1609 }
1610
1611 void User::SendAll(const char* command, char* text, ...)
1612 {
1613         char textbuffer[MAXBUF];
1614         char formatbuffer[MAXBUF];
1615         va_list argsPtr;
1616
1617         va_start(argsPtr, text);
1618         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1619         va_end(argsPtr);
1620
1621         snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost(), command, textbuffer);
1622         std::string fmt = formatbuffer;
1623
1624         for (std::vector<User*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
1625         {
1626                 (*i)->Write(fmt);
1627         }
1628 }
1629
1630
1631 std::string User::ChannelList(User* source)
1632 {
1633         try
1634         {
1635                 std::string list;
1636                 for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1637                 {
1638                         /* If the target is the same as the sender, let them see all their channels.
1639                          * If the channel is NOT private/secret OR the user shares a common channel
1640                          * If the user is an oper, and the <options:operspywhois> option is set.
1641                          */
1642                         if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->IsModeSet('p')) && (!i->first->IsModeSet('s'))) || (i->first->HasUser(source))))
1643                         {
1644                                 list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" ");
1645                         }
1646                 }
1647                 return list;
1648         }
1649         catch (...)
1650         {
1651                 ServerInstance->Log(DEBUG,"Exception in User::ChannelList()");
1652                 return "";
1653         }
1654 }
1655
1656 void User::SplitChanList(User* dest, const std::string &cl)
1657 {
1658         std::string line;
1659         std::ostringstream prefix;
1660         std::string::size_type start, pos, length;
1661
1662         try
1663         {
1664                 prefix << this->nick << " " << dest->nick << " :";
1665                 line = prefix.str();
1666                 int namelen = strlen(ServerInstance->Config->ServerName) + 6;
1667
1668                 for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
1669                 {
1670                         length = (pos == std::string::npos) ? cl.length() : pos;
1671
1672                         if (line.length() + namelen + length - start > 510)
1673                         {
1674                                 ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
1675                                 line = prefix.str();
1676                         }
1677
1678                         if(pos == std::string::npos)
1679                         {
1680                                 line.append(cl.substr(start, length - start));
1681                                 break;
1682                         }
1683                         else
1684                         {
1685                                 line.append(cl.substr(start, length - start + 1));
1686                         }
1687                 }
1688
1689                 if (line.length())
1690                 {
1691                         ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
1692                 }
1693         }
1694
1695         catch (...)
1696         {
1697                 ServerInstance->Log(DEBUG,"Exception in User::SplitChanList()");
1698         }
1699 }
1700
1701 unsigned int User::GetMaxChans()
1702 {
1703         return this->MaxChans;
1704 }
1705
1706 /* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
1707  * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
1708  * then their ip will be taken as 'priority' anyway, so for example,
1709  * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
1710  */
1711 ConnectClass* User::GetClass(const std::string &explicit_name)
1712 {
1713         if (!explicit_name.empty())
1714         {
1715                 for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
1716                 {
1717                         if (explicit_name == i->GetName())
1718                                 return &(*i);
1719                 }
1720         }
1721         else
1722         {
1723                 for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
1724                 {
1725                         if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))
1726                         {
1727                                 if (i->GetPort())
1728                                 {
1729                                         if (this->GetPort() == i->GetPort())
1730                                                 return &(*i);
1731                                         else
1732                                                 continue;
1733                                 }
1734                                 else
1735                                         return &(*i);
1736                         }
1737                 }
1738         }
1739         return NULL;
1740 }
1741
1742 void User::PurgeEmptyChannels()
1743 {
1744         std::vector<Channel*> to_delete;
1745
1746         // firstly decrement the count on each channel
1747         for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
1748         {
1749                 f->first->RemoveAllPrefixes(this);
1750                 if (f->first->DelUser(this) == 0)
1751                 {
1752                         /* No users left in here, mark it for deletion */
1753                         try
1754                         {
1755                                 to_delete.push_back(f->first);
1756                         }
1757                         catch (...)
1758                         {
1759                                 ServerInstance->Log(DEBUG,"Exception in User::PurgeEmptyChannels to_delete.push_back()");
1760                         }
1761                 }
1762         }
1763
1764         for (std::vector<Channel*>::iterator n = to_delete.begin(); n != to_delete.end(); n++)
1765         {
1766                 Channel* thischan = *n;
1767                 chan_hash::iterator i2 = ServerInstance->chanlist->find(thischan->name);
1768                 if (i2 != ServerInstance->chanlist->end())
1769                 {
1770                         FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(i2->second));
1771                         DELETE(i2->second);
1772                         ServerInstance->chanlist->erase(i2);
1773                         this->chans.erase(*n);
1774                 }
1775         }
1776
1777         this->UnOper();
1778 }
1779
1780 void User::ShowMOTD()
1781 {
1782         if (!ServerInstance->Config->MOTD.size())
1783         {
1784                 this->WriteServ("422 %s :Message of the day file is missing.",this->nick);
1785                 return;
1786         }
1787         this->WriteServ("375 %s :%s message of the day", this->nick, ServerInstance->Config->ServerName);
1788
1789         for (file_cache::iterator i = ServerInstance->Config->MOTD.begin(); i != ServerInstance->Config->MOTD.end(); i++)
1790                 this->WriteServ("372 %s :- %s",this->nick,i->c_str());
1791
1792         this->WriteServ("376 %s :End of message of the day.", this->nick);
1793 }
1794
1795 void User::ShowRULES()
1796 {
1797         if (!ServerInstance->Config->RULES.size())
1798         {
1799                 this->WriteServ("434 %s :RULES File is missing",this->nick);
1800                 return;
1801         }
1802
1803         this->WriteServ("308 %s :- %s Server Rules -",this->nick,ServerInstance->Config->ServerName);
1804
1805         for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++)
1806                 this->WriteServ("232 %s :- %s",this->nick,i->c_str());
1807
1808         this->WriteServ("309 %s :End of RULES command.",this->nick);
1809 }
1810
1811 void User::HandleEvent(EventType et, int errornum)
1812 {
1813         /* WARNING: May delete this user! */
1814         int thisfd = this->GetFd();
1815
1816         try
1817         {
1818                 switch (et)
1819                 {
1820                         case EVENT_READ:
1821                                 ServerInstance->ProcessUser(this);
1822                         break;
1823                         case EVENT_WRITE:
1824                                 this->FlushWriteBuf();
1825                         break;
1826                         case EVENT_ERROR:
1827                                 /** This should be safe, but dont DARE do anything after it -- Brain */
1828                                 this->SetWriteError(errornum ? strerror(errornum) : "EOF from client");
1829                         break;
1830                 }
1831         }
1832         catch (...)
1833         {
1834                 ServerInstance->Log(DEBUG,"Exception in User::HandleEvent intercepted");
1835         }
1836
1837         /* If the user has raised an error whilst being processed, quit them now we're safe to */
1838         if ((ServerInstance->SE->GetRef(thisfd) == this))
1839         {
1840                 if (!WriteError.empty())
1841                 {
1842                         User::QuitUser(ServerInstance, this, GetWriteError());
1843                 }
1844         }
1845 }
1846
1847 void User::SetOperQuit(const std::string &oquit)
1848 {
1849         if (operquit)
1850                 return;
1851
1852         operquit = strdup(oquit.c_str());
1853 }
1854
1855 const char* User::GetOperQuit()
1856 {
1857         return operquit ? operquit : "";
1858 }
1859
1860 void User::IncreasePenalty(int increase)
1861 {
1862         this->Penalty += increase;
1863 }
1864
1865 void User::DecreasePenalty(int decrease)
1866 {
1867         this->Penalty -= decrease;
1868 }
1869
1870 VisData::VisData()
1871 {
1872 }
1873
1874 VisData::~VisData()
1875 {
1876 }
1877
1878 bool VisData::VisibleTo(User* user)
1879 {
1880         return true;
1881 }
1882