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