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