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