]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/users.cpp
Move more stuff into userrec
[user/henk/code/inspircd.git] / src / users.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                     E-mail:
7  *              <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *          the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 #include "inspircd_config.h"
18 #include "configreader.h"
19 #include "channels.h"
20 #include "connection.h"
21 #include "users.h"
22 #include "inspircd.h"
23 #include <stdarg.h>
24 #include "inspstring.h"
25 #include "commands.h"
26 #include "helperfuncs.h"
27 #include "typedefs.h"
28 #include "socketengine.h"
29 #include "hashcomp.h"
30 #include "message.h"
31 #include "wildcard.h"
32 #include "xline.h"
33 #include "cull_list.h"
34
35 extern std::vector<Module*> modules;
36 extern std::vector<ircd_module*> factory;
37 extern int MODCOUNT;
38 extern time_t TIME;
39 extern Server* MyServer;
40
41 irc::whowas::whowas_users whowas;
42 static unsigned long already_sent[MAX_DESCRIPTORS] = {0};
43 std::vector<userrec*> all_opers;
44
45 typedef std::map<irc::string,char*> opertype_t;
46 typedef opertype_t operclass_t;
47
48 opertype_t opertypes;
49 operclass_t operclass;
50
51 /* XXX: Used for speeding up WriteCommon operations */
52 unsigned long uniq_id = 0;
53
54 bool InitTypes(ServerConfig* conf, const char* tag)
55 {
56         for (opertype_t::iterator n = opertypes.begin(); n != opertypes.end(); n++)
57         {
58                 if (n->second)
59                         delete[] n->second;
60         }
61         
62         opertypes.clear();
63         return true;
64 }
65
66 bool InitClasses(ServerConfig* conf, const char* tag)
67 {
68         for (operclass_t::iterator n = operclass.begin(); n != operclass.end(); n++)
69         {
70                 if (n->second)
71                         delete[] n->second;
72         }
73         
74         operclass.clear();
75         return true;
76 }
77
78 bool DoType(ServerConfig* conf, const char* tag, char** entries, void** values, int* types)
79 {
80         char* TypeName = (char*)values[0];
81         char* Classes = (char*)values[1];
82         
83         opertypes[TypeName] = strdup(Classes);
84         log(DEBUG,"Read oper TYPE '%s' with classes '%s'",TypeName,Classes);
85         return true;
86 }
87
88 bool DoClass(ServerConfig* conf, const char* tag, char** entries, void** values, int* types)
89 {
90         char* ClassName = (char*)values[0];
91         char* CommandList = (char*)values[1];
92         
93         operclass[ClassName] = strdup(CommandList);
94         log(DEBUG,"Read oper CLASS '%s' with commands '%s'",ClassName,CommandList);
95         return true;
96 }
97
98 bool DoneClassesAndTypes(ServerConfig* conf, const char* tag)
99 {
100         return true;
101 }
102
103 bool userrec::ProcessNoticeMasks(const char *sm)
104 {
105         bool adding = true;
106         const char *c = sm;
107
108         while (c && *c)
109         {
110                 switch (*c)
111                 {
112                         case '+':
113                                 adding = true;
114                                 break;
115                         case '-':
116                                 adding = false;
117                                 break;
118                         default:
119                                 if ((*c >= 'A') && (*c <= 'z'))
120                                         this->SetNoticeMask(*c, adding);
121                                 break;
122                 }
123
124                 *c++;
125         }
126
127         return true;
128 }
129
130 void userrec::StartDNSLookup()
131 {
132         log(DEBUG,"Commencing reverse lookup");
133         try
134         {
135                 res_reverse = new UserResolver(ServerInstance, this, this->GetIPString(), false);
136                 MyServer->AddResolver(res_reverse);
137         }
138         catch (ModuleException& e)
139         {
140                 log(DEBUG,"Error in resolver: %s",e.GetReason());
141         }
142 }
143
144 UserResolver::UserResolver(InspIRCd* Instance, userrec* user, std::string to_resolve, bool forward) :
145         Resolver(ServerInstance, to_resolve, forward ? DNS_QUERY_FORWARD : DNS_QUERY_REVERSE), bound_user(user)
146 {
147         this->fwd = forward;
148         this->bound_fd = user->fd;
149 }
150
151 void UserResolver::OnLookupComplete(const std::string &result)
152 {
153         if ((!this->fwd) && (ServerInstance->fd_ref_table[this->bound_fd] == this->bound_user))
154         {
155                 log(DEBUG,"Commencing forward lookup");
156                 this->bound_user->stored_host = result;
157                 try
158                 {
159                         bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, true);
160                         MyServer->AddResolver(bound_user->res_forward);
161                 }
162                 catch (ModuleException& e)
163                 {
164                         log(DEBUG,"Error in resolver: %s",e.GetReason());
165                 }
166         }
167         else if ((this->fwd) && (ServerInstance->fd_ref_table[this->bound_fd] == this->bound_user))
168         {
169                 /* Both lookups completed */
170                 if (this->bound_user->GetIPString() == result)
171                 {
172                         std::string hostname = this->bound_user->stored_host;
173                         if (hostname.length() < 65)
174                         {
175                                 /* Hostnames starting with : are not a good thing (tm) */
176                                 if (*(hostname.c_str()) == ':')
177                                         hostname = "0" + hostname;
178
179                                 this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)", hostname.c_str());
180                                 this->bound_user->dns_done = true;
181                                 strlcpy(this->bound_user->dhost, hostname.c_str(),64);
182                                 strlcpy(this->bound_user->host, hostname.c_str(),64);
183                         }
184                         else
185                         {
186                                 this->bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", this->bound_user->GetIPString());
187                         }
188                 }
189                 else
190                 {
191                         this->bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", this->bound_user->GetIPString());
192                 }
193         }
194 }
195
196 void UserResolver::OnError(ResolverError e, const std::string &errormessage)
197 {
198         if (ServerInstance->fd_ref_table[this->bound_fd] == this->bound_user)
199         {
200                 /* Error message here */
201                 this->bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname, using your IP address (%s) instead.", this->bound_user->GetIPString());
202                 this->bound_user->dns_done = true;
203         }
204 }
205
206
207 bool userrec::IsNoticeMaskSet(unsigned char sm)
208 {
209         return (snomasks[sm-65]);
210 }
211
212 void userrec::SetNoticeMask(unsigned char sm, bool value)
213 {
214         snomasks[sm-65] = value;
215 }
216
217 const char* userrec::FormatNoticeMasks()
218 {
219         static char data[MAXBUF];
220         int offset = 0;
221
222         for (int n = 0; n < 64; n++)
223         {
224                 if (snomasks[n])
225                         data[offset++] = n+65;
226         }
227
228         data[offset] = 0;
229         return data;
230 }
231
232
233
234 bool userrec::IsModeSet(unsigned char m)
235 {
236         return (modes[m-65]);
237 }
238
239 void userrec::SetMode(unsigned char m, bool value)
240 {
241         modes[m-65] = value;
242 }
243
244 const char* userrec::FormatModes()
245 {
246         static char data[MAXBUF];
247         int offset = 0;
248         for (int n = 0; n < 64; n++)
249         {
250                 if (modes[n])
251                         data[offset++] = n+65;
252         }
253         data[offset] = 0;
254         return data;
255 }
256
257 userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance)
258 {
259         // the PROPER way to do it, AVOID bzero at *ALL* costs
260         *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0;
261         server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName);
262         reset_due = TIME;
263         lines_in = fd = lastping = signon = idle_lastmsg = nping = registered = 0;
264         timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0;
265         haspassed = dns_done = false;
266         recvq = "";
267         sendq = "";
268         WriteError = "";
269         res_forward = res_reverse = NULL;
270         ip = NULL;
271         chans.clear();
272         invites.clear();
273         chans.resize(MAXCHANS);
274         memset(modes,0,sizeof(modes));
275         
276         for (unsigned int n = 0; n < MAXCHANS; n++)
277         {
278                 ucrec* x = new ucrec();
279                 chans[n] = x;
280                 x->channel = NULL;
281                 x->uc_modes = 0;
282         }
283 }
284
285 userrec::~userrec()
286 {
287         for (std::vector<ucrec*>::iterator n = chans.begin(); n != chans.end(); n++)
288         {
289                 ucrec* x = (ucrec*)*n;
290                 delete x;
291         }
292
293         if (ip)
294         {
295                 if (this->GetProtocolFamily() == AF_INET)
296                 {
297                         delete (sockaddr_in*)ip;
298                 }
299 #ifdef SUPPORT_IP6LINKS
300                 else
301                 {
302                         delete (sockaddr_in6*)ip;
303                 }
304 #endif
305         }
306 }
307
308 /* XXX - minor point, other *Host functions return a char *, this one creates it. Might be nice to be consistant? */
309 void userrec::MakeHost(char* nhost)
310 {
311         /* This is much faster than snprintf */
312         char* t = nhost;
313         for(char* n = ident; *n; n++)
314                 *t++ = *n;
315         *t++ = '@';
316         for(char* n = host; *n; n++)
317                 *t++ = *n;
318         *t = 0;
319 }
320
321 void userrec::CloseSocket()
322 {
323         shutdown(this->fd,2);
324         close(this->fd);
325 }
326  
327 char* userrec::GetFullHost()
328 {
329         static char result[MAXBUF];
330         char* t = result;
331         for(char* n = nick; *n; n++)
332                 *t++ = *n;
333         *t++ = '!';
334         for(char* n = ident; *n; n++)
335                 *t++ = *n;
336         *t++ = '@';
337         for(char* n = dhost; *n; n++)
338                 *t++ = *n;
339         *t = 0;
340         return result;
341 }
342
343 char* userrec::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 userrec::ReadData(void* buffer, size_t size)
356 {
357         if (this->fd > -1)
358         {
359                 return read(this->fd, buffer, size);
360         }
361         else
362                 return 0;
363 }
364
365
366 char* userrec::GetFullRealHost()
367 {
368         static char fresult[MAXBUF];
369         char* t = fresult;
370         for(char* n = nick; *n; n++)
371                 *t++ = *n;
372         *t++ = '!';
373         for(char* n = ident; *n; n++)
374                 *t++ = *n;
375         *t++ = '@';
376         for(char* n = host; *n; n++)
377                 *t++ = *n;
378         *t = 0;
379         return fresult;
380 }
381
382 bool userrec::IsInvited(irc::string &channel)
383 {
384         for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
385         {
386                 irc::string compare = i->channel;
387                 
388                 if (compare == channel)
389                 {
390                         return true;
391                 }
392         }
393         return false;
394 }
395
396 InvitedList* userrec::GetInviteList()
397 {
398         return &invites;
399 }
400
401 void userrec::InviteTo(irc::string &channel)
402 {
403         Invited i;
404         i.channel = channel;
405         invites.push_back(i);
406 }
407
408 void userrec::RemoveInvite(irc::string &channel)
409 {
410         log(DEBUG,"Removing invites");
411         
412         if (invites.size())
413         {
414                 for (InvitedList::iterator i = invites.begin(); i != invites.end(); i++)
415                 {
416                         irc::string compare = i->channel;
417                         
418                         if (compare == channel)
419                         {
420                                 invites.erase(i);
421                                 return;
422                         }
423                 }
424         }
425 }
426
427 bool userrec::HasPermission(const std::string &command)
428 {
429         char* mycmd;
430         char* savept;
431         char* savept2;
432         
433         /*
434          * users on remote servers can completely bypass all permissions based checks.
435          * This prevents desyncs when one server has different type/class tags to another.
436          * That having been said, this does open things up to the possibility of source changes
437          * allowing remote kills, etc - but if they have access to the src, they most likely have
438          * access to the conf - so it's an end to a means either way.
439          */
440         if (!IS_LOCAL(this))
441                 return true;
442         
443         // are they even an oper at all?
444         if (*this->oper)
445         {
446                 opertype_t::iterator iter_opertype = opertypes.find(this->oper);
447                 if (iter_opertype != opertypes.end())
448                 {
449                         char* Classes = strdup(iter_opertype->second);
450                         char* myclass = strtok_r(Classes," ",&savept);
451                         while (myclass)
452                         {
453                                 operclass_t::iterator iter_operclass = operclass.find(myclass);
454                                 if (iter_operclass != operclass.end())
455                                 {
456                                         char* CommandList = strdup(iter_operclass->second);
457                                         mycmd = strtok_r(CommandList," ",&savept2);
458                                         while (mycmd)
459                                         {
460                                                 if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))
461                                                 {
462                                                         free(Classes);
463                                                         free(CommandList);
464                                                         return true;
465                                                 }
466                                                 mycmd = strtok_r(NULL," ",&savept2);
467                                         }
468                                         free(CommandList);
469                                 }
470                                 myclass = strtok_r(NULL," ",&savept);
471                         }
472                         free(Classes);
473                 }
474         }
475         return false;
476 }
477
478
479 bool userrec::AddBuffer(const std::string &a)
480 {
481         std::string b = "";
482
483         /* NB: std::string is arsey about \r and \n and tries to translate them
484          * somehow, so we CANNOT use std::string::find() here :(
485          */
486         for (std::string::const_iterator i = a.begin(); i != a.end(); i++)
487         {
488                 if (*i != '\r')
489                         b += *i;
490         }
491
492         if (b.length())
493                 recvq.append(b);
494
495         if (recvq.length() > (unsigned)this->recvqmax)
496         {
497                 this->SetWriteError("RecvQ exceeded");
498                 WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax);
499                 return false;
500         }
501
502         return true;
503 }
504
505 bool userrec::BufferIsReady()
506 {
507         return (recvq.find('\n') != std::string::npos);
508 }
509
510 void userrec::ClearBuffer()
511 {
512         recvq = "";
513 }
514
515 std::string userrec::GetBuffer()
516 {
517         if (!recvq.length())
518                 return "";
519
520         /* Strip any leading \r or \n off the string.
521          * Usually there are only one or two of these,
522          * so its is computationally cheap to do.
523          */
524         while ((*recvq.begin() == '\r') || (*recvq.begin() == '\n'))
525                 recvq.erase(recvq.begin());
526
527         for (std::string::iterator x = recvq.begin(); x != recvq.end(); x++)
528         {
529                 /* Find the first complete line, return it as the
530                  * result, and leave the recvq as whats left
531                  */
532                 if (*x == '\n')
533                 {
534                         std::string ret = std::string(recvq.begin(), x);
535                         recvq.erase(recvq.begin(), x + 1);
536                         return ret;
537                 }
538         }
539         return "";
540 }
541
542 void userrec::AddWriteBuf(const std::string &data)
543 {
544         if (*this->GetWriteError())
545                 return;
546         
547         if (sendq.length() + data.length() > (unsigned)this->sendqmax)
548         {
549                 /*
550                  * Fix by brain - Set the error text BEFORE calling writeopers, because
551                  * if we dont it'll recursively  call here over and over again trying
552                  * to repeatedly add the text to the sendq!
553                  */
554                 this->SetWriteError("SendQ exceeded");
555                 WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax);
556                 return;
557         }
558         
559         if (data.length() > 512)
560         {
561                 std::string newdata(data);
562                 newdata.resize(510);
563                 newdata.append("\r\n");
564                 sendq.append(newdata);
565         }
566         else
567         {
568                 sendq.append(data);
569         }
570 }
571
572 // send AS MUCH OF THE USERS SENDQ as we are able to (might not be all of it)
573 void userrec::FlushWriteBuf()
574 {
575         if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER))
576         {
577                 const char* tb = this->sendq.c_str();
578                 int n_sent = write(this->fd,tb,this->sendq.length());
579                 if (n_sent == -1)
580                 {
581                         if (errno != EAGAIN)
582                                 this->SetWriteError(strerror(errno));
583                 }
584                 else
585                 {
586                         // advance the queue
587                         tb += n_sent;
588                         this->sendq = tb;
589                         // update the user's stats counters
590                         this->bytes_out += n_sent;
591                         this->cmds_out++;
592                 }
593         }
594 }
595
596 void userrec::SetWriteError(const std::string &error)
597 {
598         log(DEBUG,"SetWriteError: %s",error.c_str());
599         // don't try to set the error twice, its already set take the first string.
600         if (!this->WriteError.length())
601         {
602                 log(DEBUG,"Setting error string for %s to '%s'",this->nick,error.c_str());
603                 this->WriteError = error;
604         }
605 }
606
607 const char* userrec::GetWriteError()
608 {
609         return this->WriteError.c_str();
610 }
611
612 void userrec::Oper(const std::string &opertype)
613 {
614         this->modes[UM_OPERATOR] = 1;
615         this->WriteServ("MODE %s :+o", this->nick);
616         FOREACH_MOD(I_OnOper, OnOper(this, opertype));
617         log(DEFAULT,"OPER: %s!%s@%s opered as type: %s", this->nick, this->ident, this->host, opertype.c_str());
618         strlcpy(this->oper, opertype.c_str(), NICKMAX - 1);
619         all_opers.push_back(this);
620         FOREACH_MOD(I_OnPostOper,OnPostOper(this, opertype));
621 }
622
623 void userrec::UnOper()
624 {
625         if (*this->oper)
626         {
627                 *this->oper = 0;
628                 this->modes[UM_OPERATOR] = 0;
629                 for (std::vector<userrec*>::iterator a = all_opers.begin(); a < all_opers.end(); a++)
630                 {
631                         if (*a == this)
632                         {
633                                 log(DEBUG,"Oper removed from optimization list");
634                                 all_opers.erase(a);
635                                 return;
636                         }
637                 }
638         }
639 }
640
641 void userrec::QuitUser(InspIRCd* Instance, userrec *user,const std::string &quitreason)
642 {
643         user_hash::iterator iter = Instance->clientlist.find(user->nick);
644
645 /*
646  * I'm pretty sure returning here is causing a desync when part of the net thinks a user is gone,
647  * and another part doesn't. We want to broadcast the quit/kill before bailing so the net stays in sync.
648  *
649  * I can't imagine this blowing up, so I'm commenting it out. We still check
650  * before playing with a bad iterator below in our if(). DISCUSS THIS BEFORE YOU DO ANYTHING. --w00t
651  *
652  *      if (iter == clientlist.end())
653  *              return;
654  */
655         std::string reason = quitreason;
656
657         if (reason.length() > MAXQUIT - 1)
658                 reason.resize(MAXQUIT - 1);
659         
660         if (IS_LOCAL(user))
661                 user->Write("ERROR :Closing link (%s@%s) [%s]",user->ident,user->host,reason.c_str());
662
663         if (user->registered == REG_ALL)
664         {
665                 purge_empty_chans(user);
666                 FOREACH_MOD_I(Instance,I_OnUserQuit,OnUserQuit(user,reason));
667                 user->WriteCommonExcept("QUIT :%s",reason.c_str());
668         }
669
670         if (IS_LOCAL(user))
671                 user->FlushWriteBuf();
672
673         FOREACH_MOD_I(Instance,I_OnUserDisconnect,OnUserDisconnect(user));
674
675         if (IS_LOCAL(user))
676         {
677                 if (Instance->Config->GetIOHook(user->GetPort()))
678                 {
679                         try
680                         {
681                                 Instance->Config->GetIOHook(user->GetPort())->OnRawSocketClose(user->fd);
682                         }
683                         catch (ModuleException& modexcept)
684                         {
685                                 log(DEBUG,"Module exception cought: %s",modexcept.GetReason());
686                         }
687                 }
688                 
689                 Instance->SE->DelFd(user->fd);
690                 user->CloseSocket();
691         }
692
693         /*
694          * this must come before the WriteOpers so that it doesnt try to fill their buffer with anything
695          * if they were an oper with +s.
696          *
697          * XXX -
698          * In the current implementation, we only show local quits, as we only show local connects. With 
699          * the proposed implmentation of snomasks however, this will likely change in the (near?) future.
700          */
701         if (user->registered == REG_ALL)
702         {
703                 if (IS_LOCAL(user))
704                         WriteOpers("*** Client exiting: %s!%s@%s [%s]",user->nick,user->ident,user->host,reason.c_str());
705                 user->AddToWhoWas();
706         }
707
708         if (iter != Instance->clientlist.end())
709         {
710                 log(DEBUG,"deleting user hash value %lx",(unsigned long)user);
711                 if (IS_LOCAL(user))
712                 {
713                         Instance->fd_ref_table[user->fd] = NULL;
714                         if (find(Instance->local_users.begin(),Instance->local_users.end(),user) != Instance->local_users.end())
715                                 Instance->local_users.erase(find(Instance->local_users.begin(),Instance->local_users.end(),user));
716                 }
717                 Instance->clientlist.erase(iter);
718                 DELETE(user);
719         }
720 }
721
722 namespace irc
723 {
724         namespace whowas
725         {
726
727                 WhoWasGroup::WhoWasGroup(userrec* user) : host(NULL), dhost(NULL), ident(NULL), server(NULL), gecos(NULL), signon(user->signon)
728                 {
729                         this->host = strdup(user->host);
730                         this->dhost = strdup(user->dhost);
731                         this->ident = strdup(user->ident);
732                         this->server = user->server;
733                         this->gecos = strdup(user->fullname);
734                 }
735
736                 WhoWasGroup::~WhoWasGroup()
737                 {
738                         if (host)
739                                 free(host);
740                         if (dhost)
741                                 free(dhost);
742                         if (ident)
743                                 free(ident);
744                         if (gecos)
745                                 free(gecos);
746                 }
747
748                 /* every hour, run this function which removes all entries over 3 days */
749                 void MaintainWhoWas(time_t TIME)
750                 {
751                         for (whowas_users::iterator iter = ::whowas.begin(); iter != ::whowas.end(); iter++)
752                         {
753                                 whowas_set* n = (whowas_set*)iter->second;
754                                 if (n->size())
755                                 {
756                                         while ((n->begin() != n->end()) && ((*n->begin())->signon < TIME - 259200)) // 3 days
757                                         {
758                                                 WhoWasGroup *a = *(n->begin());
759                                                 DELETE(a);
760                                                 n->erase(n->begin());
761                                         }
762                                 }
763                         }
764                 }
765         };
766 };
767
768 /* adds or updates an entry in the whowas list */
769 void userrec::AddToWhoWas()
770 {
771         irc::whowas::whowas_users::iterator iter = whowas.find(this->nick);
772
773         if (iter == whowas.end())
774         {
775                 irc::whowas::whowas_set* n = new irc::whowas::whowas_set;
776                 irc::whowas::WhoWasGroup *a = new irc::whowas::WhoWasGroup(this);
777                 n->push_back(a);
778                 whowas[this->nick] = n;
779         }
780         else
781         {
782                 irc::whowas::whowas_set* group = (irc::whowas::whowas_set*)iter->second;
783
784                 if (group->size() > 10)
785                 {
786                         irc::whowas::WhoWasGroup *a = (irc::whowas::WhoWasGroup*)*(group->begin());
787                         DELETE(a);
788                         group->pop_front();
789                 }
790
791                 irc::whowas::WhoWasGroup *a = new irc::whowas::WhoWasGroup(this);
792                 group->push_back(a);
793         }
794 }
795
796 /* add a client connection to the sockets list */
797 void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, insp_inaddr ip)
798 {
799         std::string tempnick = ConvToStr(socket) + "-unknown";
800         user_hash::iterator iter = Instance->clientlist.find(tempnick);
801         const char *ipaddr = insp_ntoa(ip);
802         userrec* _new;
803         int j = 0;
804
805         /*
806          * fix by brain.
807          * as these nicknames are 'RFC impossible', we can be sure nobody is going to be
808          * using one as a registered connection. As they are per fd, we can also safely assume
809          * that we wont have collisions. Therefore, if the nick exists in the list, its only
810          * used by a dead socket, erase the iterator so that the new client may reclaim it.
811          * this was probably the cause of 'server ignores me when i hammer it with reconnects'
812          * issue in earlier alphas/betas
813          */
814         if (iter != Instance->clientlist.end())
815         {
816                 userrec* goner = iter->second;
817                 DELETE(goner);
818                 Instance->clientlist.erase(iter);
819         }
820
821         log(DEBUG,"AddClient: %d %d %s",socket,port,ipaddr);
822         
823         _new = new userrec(Instance);
824         Instance->clientlist[tempnick] = _new;
825         _new->fd = socket;
826         strlcpy(_new->nick,tempnick.c_str(),NICKMAX-1);
827
828         _new->server = Instance->FindServerNamePtr(Instance->Config->ServerName);
829         /* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */
830         strcpy(_new->ident, "unknown");
831
832         _new->registered = REG_NONE;
833         _new->signon = TIME + Instance->Config->dns_timeout;
834         _new->lastping = 1;
835
836         log(DEBUG,"Setting socket addresses");
837         _new->SetSockAddr(AF_FAMILY, ipaddr, port);
838         log(DEBUG,"Socket addresses set.");
839
840         /* Smarter than your average bear^H^H^H^Hset of strlcpys. */
841         for (const char* temp = _new->GetIPString(); *temp && j < 64; temp++, j++)
842                 _new->dhost[j] = _new->host[j] = *temp;
843         _new->dhost[j] = _new->host[j] = 0;
844                         
845         // set the registration timeout for this user
846         unsigned long class_regtimeout = 90;
847         int class_flood = 0;
848         long class_threshold = 5;
849         long class_sqmax = 262144;      // 256kb
850         long class_rqmax = 4096;        // 4k
851
852         for (ClassVector::iterator i = Instance->Config->Classes.begin(); i != Instance->Config->Classes.end(); i++)
853         {
854                 if ((i->type == CC_ALLOW) && (match(ipaddr,i->host.c_str(),true)))
855                 {
856                         class_regtimeout = (unsigned long)i->registration_timeout;
857                         class_flood = i->flood;
858                         _new->pingmax = i->pingtime;
859                         class_threshold = i->threshold;
860                         class_sqmax = i->sendqmax;
861                         class_rqmax = i->recvqmax;
862                         break;
863                 }
864         }
865
866         _new->nping = TIME + _new->pingmax + Instance->Config->dns_timeout;
867         _new->timeout = TIME+class_regtimeout;
868         _new->flood = class_flood;
869         _new->threshold = class_threshold;
870         _new->sendqmax = class_sqmax;
871         _new->recvqmax = class_rqmax;
872
873         Instance->fd_ref_table[socket] = _new;
874         Instance->local_users.push_back(_new);
875
876         if (Instance->local_users.size() > Instance->Config->SoftLimit)
877         {
878                 userrec::QuitUser(Instance, _new,"No more connections allowed");
879                 return;
880         }
881
882         if (Instance->local_users.size() >= MAXCLIENTS)
883         {
884                 userrec::QuitUser(Instance, _new,"No more connections allowed");
885                 return;
886         }
887
888         /*
889          * XXX -
890          * this is done as a safety check to keep the file descriptors within range of fd_ref_table.
891          * its a pretty big but for the moment valid assumption:
892          * file descriptors are handed out starting at 0, and are recycled as theyre freed.
893          * therefore if there is ever an fd over 65535, 65536 clients must be connected to the
894          * irc server at once (or the irc server otherwise initiating this many connections, files etc)
895          * which for the time being is a physical impossibility (even the largest networks dont have more
896          * than about 10,000 users on ONE server!)
897          */
898         if ((unsigned)socket >= MAX_DESCRIPTORS)
899         {
900                 userrec::QuitUser(Instance, _new,"Server is full");
901                 return;
902         }
903         char* e = matches_exception(ipaddr);
904         if (!e)
905         {
906                 char* r = matches_zline(ipaddr);
907                 if (r)
908                 {
909                         char reason[MAXBUF];
910                         snprintf(reason,MAXBUF,"Z-Lined: %s",r);
911                         userrec::QuitUser(Instance, _new,reason);
912                         return;
913                 }
914         }
915
916         if (socket > -1)
917         {
918                 if (!Instance->SE->AddFd(socket,true,X_ESTAB_CLIENT))
919                 {
920                         userrec::QuitUser(Instance, _new, "Internal error handling connection");
921                         return;
922                 }
923         }
924
925         _new->WriteServ("NOTICE Auth :*** Looking up your hostname...");
926 }
927
928 long userrec::GlobalCloneCount()
929 {
930         char u1[1024];
931         char u2[1024];
932         long x = 0;
933         for (user_hash::const_iterator a = ServerInstance->clientlist.begin(); a != ServerInstance->clientlist.end(); a++)
934         {
935                 /* We have to match ip's as strings - we don't know what protocol
936                  * a remote user may be using
937                  */
938                 if (!strcasecmp(a->second->GetIPString(u1), this->GetIPString(u2)))
939                                 x++;
940         }
941         return x;
942 }
943
944 long userrec::LocalCloneCount()
945 {
946         long x = 0;
947         for (std::vector<userrec*>::const_iterator a = ServerInstance->local_users.begin(); a != ServerInstance->local_users.end(); a++)
948         {
949                 userrec* comp = *a;
950 #ifdef IPV6
951                 /* I dont think theres any faster way of matching two ipv6 addresses than memcmp */
952                 in6_addr* s1 = &(((sockaddr_in6*)comp->ip)->sin6_addr);
953                 in6_addr* s2 = &(((sockaddr_in6*)this->ip)->sin6_addr);
954                 if (!memcmp(s1->s6_addr, s2->s6_addr, sizeof(in6_addr)))
955                         x++;
956 #else
957                 in_addr* s1 = &((sockaddr_in*)comp->ip)->sin_addr;
958                 in_addr* s2 = &((sockaddr_in*)this->ip)->sin_addr;
959                 if (s1->s_addr == s2->s_addr)
960                         x++;
961 #endif
962         }
963         return x;
964 }
965
966 void userrec::FullConnect(CullList* Goners)
967 {
968         ServerInstance->stats->statsConnects++;
969         this->idle_lastmsg = TIME;
970
971         ConnectClass a = GetClass(this);
972
973         if (a.type == CC_DENY)
974         {
975                 Goners->AddItem(this,"Unauthorised connection");
976                 return;
977         }
978         
979         if ((*(a.pass.c_str())) && (!this->haspassed))
980         {
981                 Goners->AddItem(this,"Invalid password");
982                 return;
983         }
984         
985         if (this->LocalCloneCount() > a.maxlocal)
986         {
987                 Goners->AddItem(this, "No more connections allowed from your host via this connect class (local)");
988                 WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a.maxlocal, this->GetIPString());
989                 return;
990         }
991         else if (this->GlobalCloneCount() > a.maxglobal)
992         {
993                 Goners->AddItem(this, "No more connections allowed from your host via this connect class (global)");
994                 WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s",a.maxglobal, this->GetIPString());
995                 return;
996         }
997
998         char match_against[MAXBUF];
999         snprintf(match_against,MAXBUF,"%s@%s", this->ident, this->host);
1000         char* e = matches_exception(match_against);
1001
1002         if (!e)
1003         {
1004                 char* r = matches_gline(match_against);
1005                 
1006                 if (r)
1007                 {
1008                         char reason[MAXBUF];
1009                         snprintf(reason,MAXBUF,"G-Lined: %s",r);
1010                         Goners->AddItem(this, reason);
1011                         return;
1012                 }
1013                 
1014                 r = matches_kline(match_against);
1015                 
1016                 if (r)
1017                 {
1018                         char reason[MAXBUF];
1019                         snprintf(reason,MAXBUF,"K-Lined: %s",r);
1020                         Goners->AddItem(this, reason);
1021                         return;
1022                 }
1023         }
1024
1025
1026         this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network);
1027         this->WriteServ("001 %s :Welcome to the %s IRC Network %s!%s@%s",this->nick, ServerInstance->Config->Network, this->nick, this->ident, this->host);
1028         this->WriteServ("002 %s :Your host is %s, running version %s",this->nick,ServerInstance->Config->ServerName,VERSION);
1029         this->WriteServ("003 %s :This server was created %s %s", this->nick, __TIME__, __DATE__);
1030         this->WriteServ("004 %s %s %s %s %s %s", this->nick, ServerInstance->Config->ServerName, VERSION, ServerInstance->ModeGrok->UserModeList().c_str(), ServerInstance->ModeGrok->ChannelModeList().c_str(), ServerInstance->ModeGrok->ParaModeList().c_str());
1031
1032         // anfl @ #ratbox, efnet reminded me that according to the RFC this cant contain more than 13 tokens per line...
1033         // so i'd better split it :)
1034         std::stringstream out(ServerInstance->Config->data005);
1035         std::string token = "";
1036         std::string line5 = "";
1037         int token_counter = 0;
1038         
1039         while (!out.eof())
1040         {
1041                 out >> token;
1042                 line5 = line5 + token + " ";
1043                 token_counter++;
1044                 
1045                 if ((token_counter >= 13) || (out.eof() == true))
1046                 {
1047                         this->WriteServ("005 %s %s:are supported by this server", this->nick, line5.c_str());
1048                         line5 = "";
1049                         token_counter = 0;
1050                 }
1051         }
1052         
1053         ShowMOTD(this);
1054
1055         /*
1056          * fix 3 by brain, move registered = 7 below these so that spurious modes and host
1057          * changes dont go out onto the network and produce 'fake direction'.
1058          */
1059         FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
1060         FOREACH_MOD(I_OnGlobalConnect,OnGlobalConnect(this));
1061         this->registered = REG_ALL;
1062         WriteOpers("*** Client connecting on port %d: %s!%s@%s [%s]", this->GetPort(), this->nick, this->ident, this->host, this->GetIPString());
1063 }
1064
1065 /** userrec::UpdateNick()
1066  * re-allocates a nick in the user_hash after they change nicknames,
1067  * returns a pointer to the new user as it may have moved
1068  */
1069 userrec* userrec::UpdateNickHash(const char* New)
1070 {
1071         //user_hash::iterator newnick;
1072         user_hash::iterator oldnick = ServerInstance->clientlist.find(this->nick);
1073
1074         if (!strcasecmp(this->nick,New))
1075                 return oldnick->second;
1076
1077         if (oldnick == ServerInstance->clientlist.end())
1078                 return NULL; /* doesnt exist */
1079
1080         userrec* olduser = oldnick->second;
1081         ServerInstance->clientlist[New] = olduser;
1082         ServerInstance->clientlist.erase(oldnick);
1083         return ServerInstance->clientlist[New];
1084 }
1085
1086 bool userrec::ForceNickChange(const char* newnick)
1087 {
1088         char nick[MAXBUF];
1089         int MOD_RESULT = 0;
1090
1091         *nick = 0;
1092
1093         FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(this, newnick));
1094         
1095         if (MOD_RESULT)
1096         {
1097                 ServerInstance->stats->statsCollisions++;
1098                 return false;
1099         }
1100         
1101         if (matches_qline(newnick))
1102         {
1103                 ServerInstance->stats->statsCollisions++;
1104                 return false;
1105         }
1106
1107         if (newnick)
1108         {
1109                 strlcpy(this->nick, newnick, NICKMAX - 1);
1110         }
1111         if (this->registered == REG_ALL)
1112         {
1113                 const char* pars[1];
1114                 pars[0] = nick;
1115                 std::string cmd = "NICK";
1116                 ServerInstance->Parser->CallHandler(cmd, pars, 1, this);
1117         }
1118         return true;
1119 }
1120
1121 void userrec::SetSockAddr(int protocol_family, const char* ip, int port)
1122 {
1123         switch (protocol_family)
1124         {
1125 #ifdef SUPPORT_IP6LINKS
1126                 case AF_INET6:
1127                 {
1128                         log(DEBUG,"Set inet6 protocol address");
1129                         sockaddr_in6* sin = new sockaddr_in6;
1130                         sin->sin6_family = AF_INET6;
1131                         sin->sin6_port = port;
1132                         inet_pton(AF_INET6, ip, &sin->sin6_addr);
1133                         this->ip = (sockaddr*)sin;
1134                 }
1135                 break;
1136 #endif
1137                 case AF_INET:
1138                 {
1139                         log(DEBUG,"Set inet4 protocol address");
1140                         sockaddr_in* sin = new sockaddr_in;
1141                         sin->sin_family = AF_INET;
1142                         sin->sin_port = port;
1143                         inet_pton(AF_INET, ip, &sin->sin_addr);
1144                         this->ip = (sockaddr*)sin;
1145                 }
1146                 break;
1147                 default:
1148                         log(DEBUG,"Ut oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);
1149                 break;
1150         }
1151 }
1152
1153 int userrec::GetPort()
1154 {
1155         if (this->ip == NULL)
1156                 return 0;
1157
1158         switch (this->GetProtocolFamily())
1159         {
1160 #ifdef SUPPORT_IP6LINKS
1161                 case AF_INET6:
1162                 {
1163                         sockaddr_in6* sin = (sockaddr_in6*)this->ip;
1164                         return sin->sin6_port;
1165                 }
1166                 break;
1167 #endif
1168                 case AF_INET:
1169                 {
1170                         sockaddr_in* sin = (sockaddr_in*)this->ip;
1171                         return sin->sin_port;
1172                 }
1173                 break;
1174                 default:
1175                         log(DEBUG,"Ut oh, '%s' has an unknown protocol family!",this->nick);
1176                 break;
1177         }
1178         return 0;
1179 }
1180
1181 int userrec::GetProtocolFamily()
1182 {
1183         if (this->ip == NULL)
1184                 return 0;
1185
1186         sockaddr_in* sin = (sockaddr_in*)this->ip;
1187         return sin->sin_family;
1188 }
1189
1190 const char* userrec::GetIPString()
1191 {
1192         static char buf[1024];
1193         static char temp[1024];
1194
1195         if (this->ip == NULL)
1196                 return "";
1197
1198         switch (this->GetProtocolFamily())
1199         {
1200 #ifdef SUPPORT_IP6LINKS
1201                 case AF_INET6:
1202                 {
1203                         sockaddr_in6* sin = (sockaddr_in6*)this->ip;
1204                         inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
1205                         /* IP addresses starting with a : on irc are a Bad Thing (tm) */
1206                         if (*buf == ':')
1207                         {
1208                                 strlcpy(&temp[1], buf, sizeof(temp));
1209                                 *temp = '0';
1210                                 return temp;
1211                         }
1212                         return buf;
1213                 }
1214                 break;
1215 #endif
1216                 case AF_INET:
1217                 {
1218                         sockaddr_in* sin = (sockaddr_in*)this->ip;
1219                         inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
1220                         return buf;
1221                 }
1222                 break;
1223                 default:
1224                         log(DEBUG,"Ut oh, '%s' has an unknown protocol family!",this->nick);
1225                 break;
1226         }
1227         return "";
1228 }
1229
1230 const char* userrec::GetIPString(char* buf)
1231 {
1232         static char temp[1024];
1233
1234         if (this->ip == NULL)
1235         {
1236                 *buf = 0;
1237                 return buf;
1238         }
1239
1240         switch (this->GetProtocolFamily())
1241         {
1242 #ifdef SUPPORT_IP6LINKS
1243                 case AF_INET6:
1244                 {
1245                         sockaddr_in6* sin = (sockaddr_in6*)this->ip;
1246                         inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
1247                         /* IP addresses starting with a : on irc are a Bad Thing (tm) */
1248                         if (*buf == ':')
1249                         {
1250                                 strlcpy(&temp[1], buf, sizeof(temp));
1251                                 *temp = '0';
1252                                 strlcpy(buf, temp, sizeof(temp));
1253                         }
1254                         return buf;
1255                 }
1256                 break;
1257 #endif
1258                 case AF_INET:
1259                 {
1260                         sockaddr_in* sin = (sockaddr_in*)this->ip;
1261                         inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
1262                         return buf;
1263                 }
1264                 break;
1265
1266                 default:
1267                         log(DEBUG,"Ut oh, '%s' has an unknown protocol family!",this->nick);
1268                 break;
1269         }
1270         return "";
1271 }
1272
1273
1274 void userrec::Write(const std::string &text)
1275 {
1276         if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS))
1277                 return;
1278
1279         std::string crlf = text;
1280         crlf.append("\r\n");
1281
1282         if (ServerInstance->Config->GetIOHook(this->GetPort()))
1283         {
1284                 try
1285                 {
1286                         ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, crlf.data(), crlf.length());
1287                 }
1288                 catch (ModuleException& modexcept)
1289                 {
1290                         log(DEBUG,"Module exception caught: %s",modexcept.GetReason());
1291                 }
1292         }
1293         else
1294         {
1295                 this->AddWriteBuf(crlf);
1296         }
1297         ServerInstance->stats->statsSent += crlf.length();
1298 }
1299
1300 /** Write()
1301  */
1302 void userrec::Write(const char *text, ...)
1303 {
1304         va_list argsPtr;
1305         char textbuffer[MAXBUF];
1306
1307         va_start(argsPtr, text);
1308         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1309         va_end(argsPtr);
1310
1311         this->Write(std::string(textbuffer));
1312 }
1313
1314 void userrec::WriteServ(const std::string& text)
1315 {
1316         char textbuffer[MAXBUF];
1317
1318         snprintf(textbuffer,MAXBUF,":%s %s",ServerInstance->Config->ServerName,text.c_str());
1319         this->Write(std::string(textbuffer));
1320 }
1321
1322 /** WriteServ()
1323  *  Same as Write(), except `text' is prefixed with `:server.name '.
1324  */
1325 void userrec::WriteServ(const char* text, ...)
1326 {
1327         va_list argsPtr;
1328         char textbuffer[MAXBUF];
1329
1330         va_start(argsPtr, text);
1331         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1332         va_end(argsPtr);
1333
1334         this->WriteServ(std::string(textbuffer));
1335 }
1336
1337
1338 void userrec::WriteFrom(userrec *user, const std::string &text)
1339 {
1340         char tb[MAXBUF];
1341
1342         snprintf(tb,MAXBUF,":%s %s",user->GetFullHost(),text.c_str());
1343         
1344         this->Write(std::string(tb));
1345 }
1346
1347
1348 /* write text from an originating user to originating user */
1349
1350 void userrec::WriteFrom(userrec *user, const char* text, ...)
1351 {
1352         va_list argsPtr;
1353         char textbuffer[MAXBUF];
1354
1355         va_start(argsPtr, text);
1356         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1357         va_end(argsPtr);
1358
1359         this->WriteFrom(user, std::string(textbuffer));
1360 }
1361
1362
1363 /* write text to an destination user from a source user (e.g. user privmsg) */
1364
1365 void userrec::WriteTo(userrec *dest, const char *data, ...)
1366 {
1367         char textbuffer[MAXBUF];
1368         va_list argsPtr;
1369
1370         va_start(argsPtr, data);
1371         vsnprintf(textbuffer, MAXBUF, data, argsPtr);
1372         va_end(argsPtr);
1373
1374         this->WriteTo(dest, std::string(textbuffer));
1375 }
1376
1377 void userrec::WriteTo(userrec *dest, const std::string &data)
1378 {
1379         dest->WriteFrom(this, data);
1380 }
1381
1382
1383 void userrec::WriteCommon(const char* text, ...)
1384 {
1385         char textbuffer[MAXBUF];
1386         va_list argsPtr;
1387
1388         if (this->registered != REG_ALL)
1389                 return;
1390
1391         va_start(argsPtr, text);
1392         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1393         va_end(argsPtr);
1394
1395         this->WriteCommon(std::string(textbuffer));
1396 }
1397
1398 void userrec::WriteCommon(const std::string &text)
1399 {
1400         bool sent_to_at_least_one = false;
1401
1402         if (this->registered != REG_ALL)
1403                 return;
1404
1405         uniq_id++;
1406
1407         for (std::vector<ucrec*>::const_iterator v = this->chans.begin(); v != this->chans.end(); v++)
1408         {
1409                 ucrec *n = *v;
1410                 if (n->channel)
1411                 {
1412                         CUList *ulist= n->channel->GetUsers();
1413
1414                         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1415                         {
1416                                 if ((IS_LOCAL(i->second)) && (already_sent[i->second->fd] != uniq_id))
1417                                 {
1418                                         already_sent[i->second->fd] = uniq_id;
1419                                         i->second->WriteFrom(this, std::string(text));
1420                                         sent_to_at_least_one = true;
1421                                 }
1422                         }
1423                 }
1424         }
1425
1426         /*
1427          * if the user was not in any channels, no users will receive the text. Make sure the user
1428          * receives their OWN message for WriteCommon
1429          */
1430         if (!sent_to_at_least_one)
1431         {
1432                 this->WriteFrom(this,std::string(text));
1433         }
1434 }
1435
1436
1437 /* write a formatted string to all users who share at least one common
1438  * channel, NOT including the source user e.g. for use in QUIT
1439  */
1440
1441 void userrec::WriteCommonExcept(const char* text, ...)
1442 {
1443         char textbuffer[MAXBUF];
1444         va_list argsPtr;
1445
1446         va_start(argsPtr, text);
1447         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1448         va_end(argsPtr);
1449
1450         this->WriteCommonExcept(std::string(textbuffer));
1451 }
1452
1453 void userrec::WriteCommonExcept(const std::string &text)
1454 {
1455         bool quit_munge = true;
1456         char oper_quit[MAXBUF];
1457         char textbuffer[MAXBUF];
1458
1459         strlcpy(textbuffer, text.c_str(), MAXBUF);
1460
1461         if (this->registered != REG_ALL)
1462                 return;
1463
1464         uniq_id++;
1465
1466         /* TODO: We need some form of WriteCommonExcept that will send two lines, one line to
1467          * opers and the other line to non-opers, then all this hidebans and hidesplits gunk
1468          * can go byebye.
1469          */
1470         if (ServerInstance->Config->HideSplits)
1471         {
1472                 char* check = textbuffer + 6;
1473
1474                 if (!strncasecmp(textbuffer, "QUIT :",6))
1475                 {
1476                         std::stringstream split(check);
1477                         std::string server_one;
1478                         std::string server_two;
1479
1480                         split >> server_one;
1481                         split >> server_two;
1482
1483                         if ((ServerInstance->FindServerName(server_one)) && (ServerInstance->FindServerName(server_two)))
1484                         {
1485                                 strlcpy(oper_quit,textbuffer,MAXQUIT);
1486                                 strlcpy(check,"*.net *.split",MAXQUIT);
1487                                 quit_munge = true;
1488                         }
1489                 }
1490         }
1491
1492         if ((ServerInstance->Config->HideBans) && (!quit_munge))
1493         {
1494                 if ((!strncasecmp(textbuffer, "QUIT :G-Lined:",14)) || (!strncasecmp(textbuffer, "QUIT :K-Lined:",14))
1495                 || (!strncasecmp(textbuffer, "QUIT :Q-Lined:",14)) || (!strncasecmp(textbuffer, "QUIT :Z-Lined:",14)))
1496                 {
1497                         char* check = textbuffer + 13;
1498                         strlcpy(oper_quit,textbuffer,MAXQUIT);
1499                         *check = 0;  // We don't need to strlcpy, we just chop it from the :
1500                         quit_munge = true;
1501                 }
1502         }
1503
1504         for (std::vector<ucrec*>::const_iterator v = this->chans.begin(); v != this->chans.end(); v++)
1505         {
1506                 ucrec* n = *v;
1507                 if (n->channel)
1508                 {
1509                         CUList *ulist= n->channel->GetUsers();
1510
1511                         for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
1512                         {
1513                                 if (this != i->second)
1514                                 {
1515                                         if ((IS_LOCAL(i->second)) && (already_sent[i->second->fd] != uniq_id))
1516                                         {
1517                                                 already_sent[i->second->fd] = uniq_id;
1518                                                 if (quit_munge)
1519                                                         i->second->WriteFrom(this, *i->second->oper ? std::string(oper_quit) : std::string(textbuffer));
1520                                                 else
1521                                                         i->second->WriteFrom(this, std::string(textbuffer));
1522                                         }
1523                                 }
1524                         }
1525                 }
1526         }
1527
1528 }
1529
1530 void userrec::WriteWallOps(const std::string &text)
1531 {
1532         /* Does nothing if theyre not opered */
1533         if ((!*this->oper) && (IS_LOCAL(this)))
1534                 return;
1535
1536         std::string wallop = "WALLOPS :";
1537         wallop.append(text);
1538
1539         for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
1540         {
1541                 userrec* t = *i;
1542                 if ((IS_LOCAL(t)) && (t->modes[UM_WALLOPS]))
1543                         this->WriteTo(t,wallop);
1544         }
1545 }
1546
1547 void userrec::WriteWallOps(const char* text, ...)
1548 {       
1549         char textbuffer[MAXBUF];
1550         va_list argsPtr;
1551
1552         va_start(argsPtr, text);
1553         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1554         va_end(argsPtr);                
1555                                         
1556         this->WriteWallOps(std::string(textbuffer));
1557 }                                      
1558
1559 /* return 0 or 1 depending if users u and u2 share one or more common channels
1560  * (used by QUIT, NICK etc which arent channel specific notices)
1561  *
1562  * The old algorithm in 1.0 for this was relatively inefficient, iterating over
1563  * the first users channels then the second users channels within the outer loop,
1564  * therefore it was a maximum of x*y iterations (upon returning 0 and checking
1565  * all possible iterations). However this new function instead checks against the
1566  * channel's userlist in the inner loop which is a std::map<userrec*,userrec*>
1567  * and saves us time as we already know what pointer value we are after.
1568  * Don't quote me on the maths as i am not a mathematician or computer scientist,
1569  * but i believe this algorithm is now x+(log y) maximum iterations instead.
1570  */
1571 bool userrec::SharesChannelWith(userrec *other)
1572 {
1573         if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
1574                 return false;
1575
1576         /* Outer loop */
1577         for (std::vector<ucrec*>::const_iterator i = this->chans.begin(); i != this->chans.end(); i++)
1578         {
1579                 /* Fetch the channel from the user */
1580                 ucrec* user_channel = *i;
1581
1582                 if (user_channel->channel)
1583                 {
1584                         /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
1585                          * by replacing it with a map::find which *should* be more efficient
1586                          */
1587                         if (user_channel->channel->HasUser(other))
1588                                 return true;
1589                 }
1590         }
1591         return false;
1592 }
1593
1594 int userrec::CountChannels()
1595 {
1596         int z = 0;
1597         for (std::vector<ucrec*>::const_iterator i = this->chans.begin(); i != this->chans.end(); i++)
1598                 if ((*i)->channel)
1599                         z++;
1600         return z;
1601 }
1602
1603 bool userrec::ChangeName(const char* gecos)
1604 {
1605         if (IS_LOCAL(this))
1606         {
1607                 int MOD_RESULT = 0;
1608                 FOREACH_RESULT(I_OnChangeLocalUserGECOS,OnChangeLocalUserGECOS(this,gecos));
1609                 if (MOD_RESULT)
1610                         return false;
1611                 FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
1612         }
1613         strlcpy(this->fullname,gecos,MAXGECOS+1);
1614         return true;
1615 }
1616
1617 bool userrec::ChangeDisplayedHost(const char* host)
1618 {
1619         if (IS_LOCAL(this))
1620         {
1621                 int MOD_RESULT = 0;
1622                 FOREACH_RESULT(I_OnChangeLocalUserHost,OnChangeLocalUserHost(this,host));
1623                 if (MOD_RESULT)
1624                         return false;
1625                 FOREACH_MOD(I_OnChangeHost,OnChangeHost(this,host));
1626         }
1627         strlcpy(this->dhost,host,63);
1628
1629         if (IS_LOCAL(this))
1630                 this->WriteServ("396 %s %s :is now your hidden host",this->nick,this->dhost);
1631
1632         return true;
1633 }
1634
1635 void userrec::NoticeAll(char* text, ...)
1636 {
1637         char textbuffer[MAXBUF];
1638         char formatbuffer[MAXBUF];
1639         va_list argsPtr;
1640
1641         va_start(argsPtr, text);
1642         vsnprintf(textbuffer, MAXBUF, text, argsPtr);
1643         va_end(argsPtr);
1644
1645         snprintf(formatbuffer,MAXBUF,"NOTICE $* :%s",textbuffer);
1646
1647         for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
1648         {
1649                 userrec* t = *i;
1650                 t->WriteFrom(this, std::string(formatbuffer));
1651         }
1652 }
1653
1654
1655 std::string userrec::ChannelList(userrec* source)
1656 {
1657         std::string list;
1658         for (std::vector<ucrec*>::const_iterator i = this->chans.begin(); i != this->chans.end(); i++)
1659         {
1660                 ucrec* rec = *i;
1661
1662                 if(rec->channel && rec->channel->name)
1663                 {
1664                         /* If the target is the same as the sender, let them see all their channels.
1665                          * If the channel is NOT private/secret OR the user shares a common channel
1666                          * If the user is an oper, and the <options:operspywhois> option is set.
1667                          */
1668                         if ((source == this) || (*source->oper && ServerInstance->Config->OperSpyWhois) || (((!rec->channel->modes[CM_PRIVATE]) && (!rec->channel->modes[CM_SECRET])) || (rec->channel->HasUser(source))))
1669                         {
1670                                 list.append(cmode(this, rec->channel)).append(rec->channel->name).append(" ");
1671                         }
1672                 }
1673         }
1674         return list;
1675 }
1676
1677 void userrec::SplitChanList(userrec* dest, const std::string &cl)
1678 {
1679         std::string line;
1680         std::ostringstream prefix;
1681         std::string::size_type start, pos, length;
1682
1683         prefix << ":" << ServerInstance->Config->ServerName << " 319 " << this->nick << " " << dest->nick << " :";
1684         line = prefix.str();
1685
1686         for (start = 0; (pos = cl.find(' ', start)) != std::string::npos; start = pos+1)
1687         {
1688                 length = (pos == std::string::npos) ? cl.length() : pos;
1689
1690                 if (line.length() + length - start > 510)
1691                 {
1692                         this->Write(line);
1693                         line = prefix.str();
1694                 }
1695
1696                 if(pos == std::string::npos)
1697                 {
1698                         line += cl.substr(start, length - start);
1699                         break;
1700                 }
1701                 else
1702                 {
1703                         line += cl.substr(start, length - start + 1);
1704                 }
1705         }
1706
1707         if (line.length())
1708         {
1709                 this->Write(line);
1710         }
1711 }
1712
1713