]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/users.cpp
Typo fix
[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 const char* User::GetIPString()
1143 {
1144         static char buf[1024];
1145
1146         if (this->ip == NULL)
1147                 return "";
1148
1149         switch (this->GetProtocolFamily())
1150         {
1151 #ifdef SUPPORT_IP6LINKS
1152                 case AF_INET6:
1153                 {
1154                         static char temp[1024];
1155
1156                         sockaddr_in6* sin = (sockaddr_in6*)this->ip;
1157                         inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
1158                         /* IP addresses starting with a : on irc are a Bad Thing (tm) */
1159                         if (*buf == ':')
1160                         {
1161                                 strlcpy(&temp[1], buf, sizeof(temp) - 1);
1162                                 *temp = '0';
1163                                 return temp;
1164                         }
1165                         return buf;
1166                 }
1167                 break;
1168 #endif
1169                 case AF_INET:
1170                 {
1171                         sockaddr_in* sin = (sockaddr_in*)this->ip;
1172                         inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
1173                         return buf;
1174                 }
1175                 break;
1176                 default:
1177                 break;
1178         }
1179         return "";
1180 }
1181
1182 const char* User::GetIPString(char* buf)
1183 {
1184         if (this->ip == NULL)
1185         {
1186                 *buf = 0;
1187                 return buf;
1188         }
1189
1190         switch (this->GetProtocolFamily())
1191         {
1192 #ifdef SUPPORT_IP6LINKS
1193                 case AF_INET6:
1194                 {
1195                         static char temp[1024];
1196
1197                         sockaddr_in6* sin = (sockaddr_in6*)this->ip;
1198                         inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
1199                         /* IP addresses starting with a : on irc are a Bad Thing (tm) */
1200                         if (*buf == ':')
1201                         {
1202                                 strlcpy(&temp[1], buf, sizeof(temp) - 1);
1203                                 *temp = '0';
1204                                 strlcpy(buf, temp, sizeof(temp));
1205                         }
1206                         return buf;
1207                 }
1208                 break;
1209 #endif
1210                 case AF_INET:
1211                 {
1212                         sockaddr_in* sin = (sockaddr_in*)this->ip;
1213                         inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
1214                         return buf;
1215                 }
1216                 break;
1217
1218                 default:
1219                 break;
1220         }
1221         return "";
1222 }
1223
1224 /** NOTE: We cannot pass a const reference to this method.
1225  * The string is changed by the workings of the method,
1226  * so that if we pass const ref, we end up copying it to
1227  * something we can change anyway. Makes sense to just let
1228  * the compiler do that copy for us.
1229  */
1230 void User::Write(std::string text)
1231 {
1232         if (!ServerInstance->SE->BoundsCheckFd(this))
1233                 return;
1234
1235         try
1236         {
1237                 /* ServerInstance->Log(DEBUG,"C[%d] O %s", this->GetFd(), text.c_str());
1238                  * WARNING: The above debug line is VERY loud, do NOT
1239                  * enable it till we have a good way of filtering it
1240                  * out of the logs (e.g. 1.2 would be good).
1241                  */
1242                 text.append("\r\n");
1243         }
1244         catch (...)
1245         {
1246                 ServerInstance->Log(DEBUG,"Exception in User::Write() std::string::append");
1247                 return;
1248         }
1249
1250         if (ServerInstance->Config->GetIOHook(this->GetPort()))
1251         {
1252                 try
1253                 {
1254                         /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to
1255                          * implement their own buffering mechanisms
1256                          */
1257                         ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length());
1258                 }
1259                 catch (CoreException& modexcept)
1260                 {
1261                         ServerInstance->Log(DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
1262                 }
1263         }
1264         else
1265         {
1266                 this->AddWriteBuf(text);
1267         }
1268         ServerInstance->stats->statsSent += text.length();
1269         this->ServerInstance->SE->WantWrite(this);
1270 }
1271
1272 /** Write()
1273  */
1274 void User::Write(const char *text, ...)
1275 {
1276         va_list argsPtr;
1277         char textbuffer[MAXBUF];
1278
1279         va_start(argsPtr, text);
1280         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1281         va_end(argsPtr);
1282
1283         this->Write(std::string(textbuffer));
1284 }
1285
1286 void User::WriteServ(const std::string& text)
1287 {
1288         char textbuffer[MAXBUF];
1289
1290         snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str());
1291         this->Write(std::string(textbuffer));
1292 }
1293
1294 /** WriteServ()
1295  *  Same as Write(), except `text' is prefixed with `:server.name '.
1296  */
1297 void User::WriteServ(const char* text, ...)
1298 {
1299         va_list argsPtr;
1300         char textbuffer[MAXBUF];
1301
1302         va_start(argsPtr, text);
1303         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1304         va_end(argsPtr);
1305
1306         this->WriteServ(std::string(textbuffer));
1307 }
1308
1309
1310 void User::WriteFrom(User *user, const std::string &text)
1311 {
1312         char tb[MAXBUF];
1313
1314         snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
1315
1316         this->Write(std::string(tb));
1317 }
1318
1319
1320 /* write text from an originating user to originating user */
1321
1322 void User::WriteFrom(User *user, const char* text, ...)
1323 {
1324         va_list argsPtr;
1325         char textbuffer[MAXBUF];
1326
1327         va_start(argsPtr, text);
1328         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1329         va_end(argsPtr);
1330
1331         this->WriteFrom(user, std::string(textbuffer));
1332 }
1333
1334
1335 /* write text to an destination user from a source user (e.g. user privmsg) */
1336
1337 void User::WriteTo(User *dest, const char *data, ...)
1338 {
1339         char textbuffer[MAXBUF];
1340         va_list argsPtr;
1341
1342         va_start(argsPtr, data);
1343         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
1344         va_end(argsPtr);
1345
1346         this->WriteTo(dest, std::string(textbuffer));
1347 }
1348
1349 void User::WriteTo(User *dest, const std::string &data)
1350 {
1351         dest->WriteFrom(this, data);
1352 }
1353
1354
1355 void User::WriteCommon(const char* text, ...)
1356 {
1357         char textbuffer[MAXBUF];
1358         va_list argsPtr;
1359
1360         if (this->registered != REG_ALL)
1361                 return;
1362
1363         va_start(argsPtr, text);
1364         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1365         va_end(argsPtr);
1366
1367         this->WriteCommon(std::string(textbuffer));
1368 }
1369
1370 void User::WriteCommon(const std::string &text)
1371 {
1372         try
1373         {
1374                 bool sent_to_at_least_one = false;
1375                 char tb[MAXBUF];
1376
1377                 if (this->registered != REG_ALL)
1378                         return;
1379
1380                 uniq_id++;
1381
1382                 /* We dont want to be doing this n times, just once */
1383                 snprintf(tb,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
1384                 std::string out = tb;
1385
1386                 for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
1387                 {
1388                         CUList* ulist = v->first->GetUsers();
1389                         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1390                         {
1391                                 if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
1392                                 {
1393                                         already_sent[i->first->fd] = uniq_id;
1394                                         i->first->Write(out);
1395                                         sent_to_at_least_one = true;
1396                                 }
1397                         }
1398                 }
1399
1400                 /*
1401                  * if the user was not in any channels, no users will receive the text. Make sure the user
1402                  * receives their OWN message for WriteCommon
1403                  */
1404                 if (!sent_to_at_least_one)
1405                 {
1406                         this->Write(std::string(tb));
1407                 }
1408         }
1409
1410         catch (...)
1411         {
1412                 ServerInstance->Log(DEBUG,"Exception in User::WriteCommon()");
1413         }
1414 }
1415
1416
1417 /* write a formatted string to all users who share at least one common
1418  * channel, NOT including the source user e.g. for use in QUIT
1419  */
1420
1421 void User::WriteCommonExcept(const char* text, ...)
1422 {
1423         char textbuffer[MAXBUF];
1424         va_list argsPtr;
1425
1426         va_start(argsPtr, text);
1427         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1428         va_end(argsPtr);
1429
1430         this->WriteCommonExcept(std::string(textbuffer));
1431 }
1432
1433 void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
1434 {
1435         char tb1[MAXBUF];
1436         char tb2[MAXBUF];
1437
1438         if (this->registered != REG_ALL)
1439                 return;
1440
1441         uniq_id++;
1442         snprintf(tb1,MAXBUF,":%s QUIT :%s",this->GetFullHost(),normal_text.c_str());
1443         snprintf(tb2,MAXBUF,":%s QUIT :%s",this->GetFullHost(),oper_text.c_str());
1444         std::string out1 = tb1;
1445         std::string out2 = tb2;
1446
1447         for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
1448         {
1449                 CUList *ulist = v->first->GetUsers();
1450                 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1451                 {
1452                         if (this != i->first)
1453                         {
1454                                 if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
1455                                 {
1456                                         already_sent[i->first->fd] = uniq_id;
1457                                         i->first->Write(IS_OPER(i->first) ? out2 : out1);
1458                                 }
1459                         }
1460                 }
1461         }
1462 }
1463
1464 void User::WriteCommonExcept(const std::string &text)
1465 {
1466         char tb1[MAXBUF];
1467         std::string out1;
1468
1469         if (this->registered != REG_ALL)
1470                 return;
1471
1472         uniq_id++;
1473         snprintf(tb1,MAXBUF,":%s %s",this->GetFullHost(),text.c_str());
1474         out1 = tb1;
1475
1476         for (UCListIter v = this->chans.begin(); v != this->chans.end(); v++)
1477         {
1478                 CUList *ulist = v->first->GetUsers();
1479                 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1480                 {
1481                         if (this != i->first)
1482                         {
1483                                 if ((IS_LOCAL(i->first)) && (already_sent[i->first->fd] != uniq_id))
1484                                 {
1485                                         already_sent[i->first->fd] = uniq_id;
1486                                         i->first->Write(out1);
1487                                 }
1488                         }
1489                 }
1490         }
1491
1492 }
1493
1494 void User::WriteWallOps(const std::string &text)
1495 {
1496         if (!IS_OPER(this) && IS_LOCAL(this))
1497                 return;
1498
1499         std::string wallop("WALLOPS :");
1500         wallop.append(text);
1501
1502         for (std::vector<User*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
1503         {
1504                 User* t = *i;
1505                 if (t->IsModeSet('w'))
1506                         this->WriteTo(t,wallop);
1507         }
1508 }
1509
1510 void User::WriteWallOps(const char* text, ...)
1511 {
1512         char textbuffer[MAXBUF];
1513         va_list argsPtr;
1514
1515         va_start(argsPtr, text);
1516         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1517         va_end(argsPtr);
1518
1519         this->WriteWallOps(std::string(textbuffer));
1520 }
1521
1522 /* return 0 or 1 depending if users u and u2 share one or more common channels
1523  * (used by QUIT, NICK etc which arent channel specific notices)
1524  *
1525  * The old algorithm in 1.0 for this was relatively inefficient, iterating over
1526  * the first users channels then the second users channels within the outer loop,
1527  * therefore it was a maximum of x*y iterations (upon returning 0 and checking
1528  * all possible iterations). However this new function instead checks against the
1529  * channel's userlist in the inner loop which is a std::map<User*,User*>
1530  * and saves us time as we already know what pointer value we are after.
1531  * Don't quote me on the maths as i am not a mathematician or computer scientist,
1532  * but i believe this algorithm is now x+(log y) maximum iterations instead.
1533  */
1534 bool User::SharesChannelWith(User *other)
1535 {
1536         if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
1537                 return false;
1538
1539         /* Outer loop */
1540         for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1541         {
1542                 /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
1543                  * by replacing it with a map::find which *should* be more efficient
1544                  */
1545                 if (i->first->HasUser(other))
1546                         return true;
1547         }
1548         return false;
1549 }
1550
1551 bool User::ChangeName(const char* gecos)
1552 {
1553         if (!strcmp(gecos, this->fullname))
1554                 return true;
1555
1556         if (IS_LOCAL(this))
1557         {
1558                 int MOD_RESULT = 0;
1559                 FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos));
1560                 if (MOD_RESULT)
1561                         return false;
1562                 FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
1563         }
1564         strlcpy(this->fullname,gecos,MAXGECOS+1);
1565
1566         return true;
1567 }
1568
1569 bool User::ChangeDisplayedHost(const char* host)
1570 {
1571         if (!strcmp(host, this->dhost))
1572                 return true;
1573
1574         if (IS_LOCAL(this))
1575         {
1576                 int MOD_RESULT = 0;
1577                 FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host));
1578                 if (MOD_RESULT)
1579                         return false;
1580                 FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host));
1581         }
1582         if (this->ServerInstance->Config->CycleHosts)
1583                 this->WriteCommonExcept("QUIT :Changing hosts");
1584
1585         /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
1586         strlcpy(this->dhost,host,64);
1587
1588         this->InvalidateCache();
1589
1590         if (this->ServerInstance->Config->CycleHosts)
1591         {
1592                 for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1593                 {
1594                         i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
1595                         std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
1596                         if (n.length() > 0)
1597                                 i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
1598                 }
1599         }
1600
1601         if (IS_LOCAL(this))
1602                 this->WriteServ("396 %s %s :is now your displayed host",this->nick,this->dhost);
1603
1604         return true;
1605 }
1606
1607 bool User::ChangeIdent(const char* newident)
1608 {
1609         if (!strcmp(newident, this->ident))
1610                 return true;
1611
1612         if (this->ServerInstance->Config->CycleHosts)
1613                 this->WriteCommonExcept("%s","QUIT :Changing ident");
1614
1615         strlcpy(this->ident, newident, IDENTMAX+2);
1616
1617         this->InvalidateCache();
1618
1619         if (this->ServerInstance->Config->CycleHosts)
1620         {
1621                 for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1622                 {
1623                         i->first->WriteAllExceptSender(this, false, 0, "JOIN %s", i->first->name);
1624                         std::string n = this->ServerInstance->Modes->ModeString(this, i->first);
1625                         if (n.length() > 0)
1626                                 i->first->WriteAllExceptSender(this, true, 0, "MODE %s +%s", i->first->name, n.c_str());
1627                 }
1628         }
1629
1630         return true;
1631 }
1632
1633 void User::SendAll(const char* command, char* text, ...)
1634 {
1635         char textbuffer[MAXBUF];
1636         char formatbuffer[MAXBUF];
1637         va_list argsPtr;
1638
1639         va_start(argsPtr, text);
1640         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1641         va_end(argsPtr);
1642
1643         snprintf(formatbuffer,MAXBUF,":%s %s $* :%s", this->GetFullHost(), command, textbuffer);
1644         std::string fmt = formatbuffer;
1645
1646         for (std::vector<User*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
1647         {
1648                 (*i)->Write(fmt);
1649         }
1650 }
1651
1652
1653 std::string User::ChannelList(User* source)
1654 {
1655         try
1656         {
1657                 std::string list;
1658                 for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1659                 {
1660                         /* If the target is the same as the sender, let them see all their channels.
1661                          * If the channel is NOT private/secret OR the user shares a common channel
1662                          * If the user is an oper, and the <options:operspywhois> option is set.
1663                          */
1664                         if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->IsModeSet('p')) && (!i->first->IsModeSet('s'))) || (i->first->HasUser(source))))
1665                         {
1666                                 list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" ");
1667                         }
1668                 }
1669                 return list;
1670         }
1671         catch (...)
1672         {
1673                 ServerInstance->Log(DEBUG,"Exception in User::ChannelList()");
1674                 return "";
1675         }
1676 }
1677
1678 void User::SplitChanList(User* dest, const std::string &cl)
1679 {
1680         std::string line;
1681         std::ostringstream prefix;
1682         std::string::size_type start, pos, length;
1683
1684         try
1685         {
1686                 prefix << this->nick << " " << dest->nick << " :";
1687                 line = prefix.str();
1688                 int namelen = strlen(ServerInstance->Config->ServerName) + 6;
1689
1690                 for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
1691                 {
1692                         length = (pos == std::string::npos) ? cl.length() : pos;
1693
1694                         if (line.length() + namelen + length - start > 510)
1695                         {
1696                                 ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
1697                                 line = prefix.str();
1698                         }
1699
1700                         if(pos == std::string::npos)
1701                         {
1702                                 line.append(cl.substr(start, length - start));
1703                                 break;
1704                         }
1705                         else
1706                         {
1707                                 line.append(cl.substr(start, length - start + 1));
1708                         }
1709                 }
1710
1711                 if (line.length())
1712                 {
1713                         ServerInstance->SendWhoisLine(this, dest, 319, "%s", line.c_str());
1714                 }
1715         }
1716
1717         catch (...)
1718         {
1719                 ServerInstance->Log(DEBUG,"Exception in User::SplitChanList()");
1720         }
1721 }
1722
1723 unsigned int User::GetMaxChans()
1724 {
1725         return this->MaxChans;
1726 }
1727
1728 /* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
1729  * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
1730  * then their ip will be taken as 'priority' anyway, so for example,
1731  * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
1732  */
1733 ConnectClass* User::GetClass(const std::string &explicit_name)
1734 {
1735         if (!explicit_name.empty())
1736         {
1737                 for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
1738                 {
1739                         if (explicit_name == i->GetName())
1740                                 return &(*i);
1741                 }
1742         }
1743         else
1744         {
1745                 for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
1746                 {
1747                         if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))
1748                         {
1749                                 if (i->GetPort())
1750                                 {
1751                                         if (this->GetPort() == i->GetPort())
1752                                                 return &(*i);
1753                                         else
1754                                                 continue;
1755                                 }
1756                                 else
1757                                         return &(*i);
1758                         }
1759                 }
1760         }
1761         return NULL;
1762 }
1763
1764 void User::PurgeEmptyChannels()
1765 {
1766         std::vector<Channel*> to_delete;
1767
1768         // firstly decrement the count on each channel
1769         for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
1770         {
1771                 f->first->RemoveAllPrefixes(this);
1772                 if (f->first->DelUser(this) == 0)
1773                 {
1774                         /* No users left in here, mark it for deletion */
1775                         try
1776                         {
1777                                 to_delete.push_back(f->first);
1778                         }
1779                         catch (...)
1780                         {
1781                                 ServerInstance->Log(DEBUG,"Exception in User::PurgeEmptyChannels to_delete.push_back()");
1782                         }
1783                 }
1784         }
1785
1786         for (std::vector<Channel*>::iterator n = to_delete.begin(); n != to_delete.end(); n++)
1787         {
1788                 Channel* thischan = *n;
1789                 chan_hash::iterator i2 = ServerInstance->chanlist->find(thischan->name);
1790                 if (i2 != ServerInstance->chanlist->end())
1791                 {
1792                         FOREACH_MOD(I_OnChannelDelete,OnChannelDelete(i2->second));
1793                         DELETE(i2->second);
1794                         ServerInstance->chanlist->erase(i2);
1795                         this->chans.erase(*n);
1796                 }
1797         }
1798
1799         this->UnOper();
1800 }
1801
1802 void User::ShowMOTD()
1803 {
1804         if (!ServerInstance->Config->MOTD.size())
1805         {
1806                 this->WriteServ("422 %s :Message of the day file is missing.",this->nick);
1807                 return;
1808         }
1809         this->WriteServ("375 %s :%s message of the day", this->nick, ServerInstance->Config->ServerName);
1810
1811         for (file_cache::iterator i = ServerInstance->Config->MOTD.begin(); i != ServerInstance->Config->MOTD.end(); i++)
1812                 this->WriteServ("372 %s :- %s",this->nick,i->c_str());
1813
1814         this->WriteServ("376 %s :End of message of the day.", this->nick);
1815 }
1816
1817 void User::ShowRULES()
1818 {
1819         if (!ServerInstance->Config->RULES.size())
1820         {
1821                 this->WriteServ("434 %s :RULES File is missing",this->nick);
1822                 return;
1823         }
1824
1825         this->WriteServ("308 %s :- %s Server Rules -",this->nick,ServerInstance->Config->ServerName);
1826
1827         for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++)
1828                 this->WriteServ("232 %s :- %s",this->nick,i->c_str());
1829
1830         this->WriteServ("309 %s :End of RULES command.",this->nick);
1831 }
1832
1833 void User::HandleEvent(EventType et, int errornum)
1834 {
1835         /* WARNING: May delete this user! */
1836         int thisfd = this->GetFd();
1837
1838         try
1839         {
1840                 switch (et)
1841                 {
1842                         case EVENT_READ:
1843                                 ServerInstance->ProcessUser(this);
1844                         break;
1845                         case EVENT_WRITE:
1846                                 this->FlushWriteBuf();
1847                         break;
1848                         case EVENT_ERROR:
1849                                 /** This should be safe, but dont DARE do anything after it -- Brain */
1850                                 this->SetWriteError(errornum ? strerror(errornum) : "EOF from client");
1851                         break;
1852                 }
1853         }
1854         catch (...)
1855         {
1856                 ServerInstance->Log(DEBUG,"Exception in User::HandleEvent intercepted");
1857         }
1858
1859         /* If the user has raised an error whilst being processed, quit them now we're safe to */
1860         if ((ServerInstance->SE->GetRef(thisfd) == this))
1861         {
1862                 if (!WriteError.empty())
1863                 {
1864                         User::QuitUser(ServerInstance, this, GetWriteError());
1865                 }
1866         }
1867 }
1868
1869 void User::SetOperQuit(const std::string &oquit)
1870 {
1871         if (operquit)
1872                 return;
1873
1874         operquit = strdup(oquit.c_str());
1875 }
1876
1877 const char* User::GetOperQuit()
1878 {
1879         return operquit ? operquit : "";
1880 }
1881
1882 VisData::VisData()
1883 {
1884 }
1885
1886 VisData::~VisData()
1887 {
1888 }
1889
1890 bool VisData::VisibleTo(User* user)
1891 {
1892         return true;
1893 }
1894