]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/users.cpp
468c2aa36e92980402ccf29c8dd22f31c6baa803
[user/henk/code/inspircd.git] / src / users.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2006-2009 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2006-2007, 2009 Dennis Friis <peavey@inspircd.org>
7  *   Copyright (C) 2008 John Brooks <john.brooks@dereferenced.net>
8  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
9  *   Copyright (C) 2008 Oliver Lupton <oliverlupton@gmail.com>
10  *   Copyright (C) 2003-2008 Craig Edwards <craigedwards@brainbox.cc>
11  *
12  * This file is part of InspIRCd.  InspIRCd is free software: you can
13  * redistribute it and/or modify it under the terms of the GNU General Public
14  * License as published by the Free Software Foundation, version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24
25
26 #include "inspircd.h"
27 #include <stdarg.h>
28 #include "socketengine.h"
29 #include "xline.h"
30 #include "bancache.h"
31
32 already_sent_t LocalUser::already_sent_id = 0;
33
34 std::string User::ProcessNoticeMasks(const char *sm)
35 {
36         bool adding = true, oldadding = false;
37         const char *c = sm;
38         std::string output;
39
40         while (c && *c)
41         {
42                 switch (*c)
43                 {
44                         case '+':
45                                 adding = true;
46                         break;
47                         case '-':
48                                 adding = false;
49                         break;
50                         case '*':
51                                 for (unsigned char d = 'a'; d <= 'z'; d++)
52                                 {
53                                         if (!ServerInstance->SNO->masks[d - 'a'].Description.empty())
54                                         {
55                                                 if ((!IsNoticeMaskSet(d) && adding) || (IsNoticeMaskSet(d) && !adding))
56                                                 {
57                                                         if ((oldadding != adding) || (!output.length()))
58                                                                 output += (adding ? '+' : '-');
59
60                                                         this->SetNoticeMask(d, adding);
61
62                                                         output += d;
63                                                 }
64                                                 oldadding = adding;
65                                                 char u = toupper(d);
66                                                 if ((!IsNoticeMaskSet(u) && adding) || (IsNoticeMaskSet(u) && !adding))
67                                                 {
68                                                         if ((oldadding != adding) || (!output.length()))
69                                                                 output += (adding ? '+' : '-');
70
71                                                         this->SetNoticeMask(u, adding);
72
73                                                         output += u;
74                                                 }
75                                                 oldadding = adding;
76                                         }
77                                 }
78                         break;
79                         default:
80                                 if (isalpha(*c))
81                                 {
82                                         if ((!IsNoticeMaskSet(*c) && adding) || (IsNoticeMaskSet(*c) && !adding))
83                                         {
84                                                 if ((oldadding != adding) || (!output.length()))
85                                                         output += (adding ? '+' : '-');
86
87                                                 this->SetNoticeMask(*c, adding);
88
89                                                 output += *c;
90                                         }
91                                 }
92                                 else
93                                         this->WriteNumeric(ERR_UNKNOWNSNOMASK, "%s %c :is unknown snomask char to me", this->nick.c_str(), *c);
94
95                                 oldadding = adding;
96                         break;
97                 }
98
99                 c++;
100         }
101
102         std::string s = this->FormatNoticeMasks();
103         if (s.length() == 0)
104         {
105                 this->modes[UM_SNOMASK] = false;
106         }
107
108         return output;
109 }
110
111 bool User::IsNoticeMaskSet(unsigned char sm)
112 {
113         if (!isalpha(sm))
114                 return false;
115         return (snomasks[sm-65]);
116 }
117
118 void User::SetNoticeMask(unsigned char sm, bool value)
119 {
120         if (!isalpha(sm))
121                 return;
122         snomasks[sm-65] = value;
123 }
124
125 std::string User::FormatNoticeMasks()
126 {
127         std::string data;
128
129         for (unsigned char n = 0; n < 64; n++)
130         {
131                 if (snomasks[n])
132                         data.push_back(n + 65); 
133         }
134
135         return data;
136 }
137
138 bool User::IsModeSet(unsigned char m)
139 {
140         if (!isalpha(m))
141                 return false;
142         return (modes[m-65]);
143 }
144
145 void User::SetMode(unsigned char m, bool value)
146 {
147         if (!isalpha(m))
148                 return;
149         modes[m-65] = value;
150 }
151
152 const char* User::FormatModes(bool showparameters)
153 {
154         static std::string data;
155         std::string params;
156         data.clear();
157
158         for (unsigned char n = 0; n < 64; n++)
159         {
160                 if (modes[n])
161                 {
162                         data.push_back(n + 65);
163                         ModeHandler* mh = ServerInstance->Modes->FindMode(n + 65, MODETYPE_USER);
164                         if (showparameters && mh && mh->GetNumParams(true))
165                         {
166                                 std::string p = mh->GetUserParameter(this);
167                                 if (p.length())
168                                         params.append(" ").append(p);
169                         }
170                 }
171         }
172         data += params;
173         return data.c_str();
174 }
175
176 User::User(const std::string &uid, const std::string& sid, int type)
177         : uuid(uid), server(sid), usertype(type)
178 {
179         age = ServerInstance->Time();
180         signon = 0;
181         registered = 0;
182         quietquit = quitting = false;
183         client_sa.sa.sa_family = AF_UNSPEC;
184
185         ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New UUID for user: %s", uuid.c_str());
186
187         user_hash::iterator finduuid = ServerInstance->Users->uuidlist->find(uuid);
188         if (finduuid == ServerInstance->Users->uuidlist->end())
189                 (*ServerInstance->Users->uuidlist)[uuid] = this;
190         else
191                 throw CoreException("Duplicate UUID "+std::string(uuid)+" in User constructor");
192 }
193
194 LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
195         : User(ServerInstance->UIDGen.GetUID(), ServerInstance->Config->ServerName, USERTYPE_LOCAL), eh(this),
196         localuseriter(ServerInstance->Users->local_users.end()),
197         bytes_in(0), bytes_out(0), cmds_in(0), cmds_out(0), nping(0), CommandFloodPenalty(0),
198         already_sent(0)
199 {
200         exempt = quitting_sendq = false;
201         idle_lastmsg = 0;
202         ident = "unknown";
203         lastping = 0;
204         eh.SetFd(myfd);
205         memcpy(&client_sa, client, sizeof(irc::sockets::sockaddrs));
206         memcpy(&server_sa, servaddr, sizeof(irc::sockets::sockaddrs));
207         dhost = host = GetIPString();
208 }
209
210 User::~User()
211 {
212         if (ServerInstance->Users->uuidlist->find(uuid) != ServerInstance->Users->uuidlist->end())
213                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "User destructor for %s called without cull", uuid.c_str());
214 }
215
216 const std::string& User::MakeHost()
217 {
218         if (!this->cached_makehost.empty())
219                 return this->cached_makehost;
220
221         // XXX: Is there really a need to cache this?
222         this->cached_makehost = ident + "@" + host;
223         return this->cached_makehost;
224 }
225
226 const std::string& User::MakeHostIP()
227 {
228         if (!this->cached_hostip.empty())
229                 return this->cached_hostip;
230
231         // XXX: Is there really a need to cache this?
232         this->cached_hostip = ident + "@" + this->GetIPString();
233         return this->cached_hostip;
234 }
235
236 const std::string& User::GetFullHost()
237 {
238         if (!this->cached_fullhost.empty())
239                 return this->cached_fullhost;
240
241         // XXX: Is there really a need to cache this?
242         this->cached_fullhost = nick + "!" + ident + "@" + dhost;
243         return this->cached_fullhost;
244 }
245
246 const std::string& User::GetFullRealHost()
247 {
248         if (!this->cached_fullrealhost.empty())
249                 return this->cached_fullrealhost;
250
251         // XXX: Is there really a need to cache this?
252         this->cached_fullrealhost = nick + "!" + ident + "@" + host;
253         return this->cached_fullrealhost;
254 }
255
256 InviteList& LocalUser::GetInviteList()
257 {
258         RemoveExpiredInvites();
259         return invites;
260 }
261
262 bool LocalUser::RemoveInvite(Channel* chan)
263 {
264         Invitation* inv = Invitation::Find(chan, this);
265         if (inv)
266         {
267                 inv->cull();
268                 delete inv;
269                 return true;
270         }
271         return false;
272 }
273
274 void LocalUser::RemoveExpiredInvites()
275 {
276         Invitation::Find(NULL, this);
277 }
278
279 bool User::HasModePermission(unsigned char, ModeType)
280 {
281         return true;
282 }
283
284 bool LocalUser::HasModePermission(unsigned char mode, ModeType type)
285 {
286         if (!this->IsOper())
287                 return false;
288
289         if (mode < 'A' || mode > ('A' + 64)) return false;
290
291         return ((type == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
292
293 }
294 /*
295  * users on remote servers can completely bypass all permissions based checks.
296  * This prevents desyncs when one server has different type/class tags to another.
297  * That having been said, this does open things up to the possibility of source changes
298  * allowing remote kills, etc - but if they have access to the src, they most likely have
299  * access to the conf - so it's an end to a means either way.
300  */
301 bool User::HasPermission(const std::string&)
302 {
303         return true;
304 }
305
306 bool LocalUser::HasPermission(const std::string &command)
307 {
308         // are they even an oper at all?
309         if (!this->IsOper())
310         {
311                 return false;
312         }
313
314         if (oper->AllowedOperCommands.find(command) != oper->AllowedOperCommands.end())
315                 return true;
316         else if (oper->AllowedOperCommands.find("*") != oper->AllowedOperCommands.end())
317                 return true;
318
319         return false;
320 }
321
322 bool User::HasPrivPermission(const std::string &privstr, bool noisy)
323 {
324         return true;
325 }
326
327 bool LocalUser::HasPrivPermission(const std::string &privstr, bool noisy)
328 {
329         if (!this->IsOper())
330         {
331                 if (noisy)
332                         this->WriteNotice("You are not an oper");
333                 return false;
334         }
335
336         if (oper->AllowedPrivs.find(privstr) != oper->AllowedPrivs.end())
337         {
338                 return true;
339         }
340         else if (oper->AllowedPrivs.find("*") != oper->AllowedPrivs.end())
341         {
342                 return true;
343         }
344
345         if (noisy)
346                 this->WriteNotice("Oper type " + oper->name + " does not have access to priv " + privstr);
347
348         return false;
349 }
350
351 void UserIOHandler::OnDataReady()
352 {
353         if (user->quitting)
354                 return;
355
356         if (recvq.length() > user->MyClass->GetRecvqMax() && !user->HasPrivPermission("users/flood/increased-buffers"))
357         {
358                 ServerInstance->Users->QuitUser(user, "RecvQ exceeded");
359                 ServerInstance->SNO->WriteToSnoMask('a', "User %s RecvQ of %lu exceeds connect class maximum of %lu",
360                         user->nick.c_str(), (unsigned long)recvq.length(), user->MyClass->GetRecvqMax());
361                 return;
362         }
363         unsigned long sendqmax = ULONG_MAX;
364         if (!user->HasPrivPermission("users/flood/increased-buffers"))
365                 sendqmax = user->MyClass->GetSendqSoftMax();
366         unsigned long penaltymax = ULONG_MAX;
367         if (!user->HasPrivPermission("users/flood/no-fakelag"))
368                 penaltymax = user->MyClass->GetPenaltyThreshold() * 1000;
369
370         while (user->CommandFloodPenalty < penaltymax && getSendQSize() < sendqmax)
371         {
372                 std::string line;
373                 line.reserve(ServerInstance->Config->Limits.MaxLine);
374                 std::string::size_type qpos = 0;
375                 while (qpos < recvq.length())
376                 {
377                         char c = recvq[qpos++];
378                         switch (c)
379                         {
380                         case '\0':
381                                 c = ' ';
382                                 break;
383                         case '\r':
384                                 continue;
385                         case '\n':
386                                 goto eol_found;
387                         }
388                         if (line.length() < ServerInstance->Config->Limits.MaxLine - 2)
389                                 line.push_back(c);
390                 }
391                 // if we got here, the recvq ran out before we found a newline
392                 return;
393 eol_found:
394                 // just found a newline. Terminate the string, and pull it out of recvq
395                 recvq = recvq.substr(qpos);
396
397                 // TODO should this be moved to when it was inserted in recvq?
398                 ServerInstance->stats->statsRecv += qpos;
399                 user->bytes_in += qpos;
400                 user->cmds_in++;
401
402                 ServerInstance->Parser->ProcessBuffer(line, user);
403                 if (user->quitting)
404                         return;
405         }
406         if (user->CommandFloodPenalty >= penaltymax && !user->MyClass->fakelag)
407                 ServerInstance->Users->QuitUser(user, "Excess Flood");
408 }
409
410 void UserIOHandler::AddWriteBuf(const std::string &data)
411 {
412         if (user->quitting_sendq)
413                 return;
414         if (!user->quitting && getSendQSize() + data.length() > user->MyClass->GetSendqHardMax() &&
415                 !user->HasPrivPermission("users/flood/increased-buffers"))
416         {
417                 user->quitting_sendq = true;
418                 ServerInstance->GlobalCulls.AddSQItem(user);
419                 return;
420         }
421
422         // We still want to append data to the sendq of a quitting user,
423         // e.g. their ERROR message that says 'closing link'
424
425         WriteData(data);
426 }
427
428 void UserIOHandler::OnError(BufferedSocketError)
429 {
430         ServerInstance->Users->QuitUser(user, getError());
431 }
432
433 CullResult User::cull()
434 {
435         if (!quitting)
436                 ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
437         PurgeEmptyChannels();
438
439         if (client_sa.sa.sa_family != AF_UNSPEC)
440                 ServerInstance->Users->RemoveCloneCounts(this);
441
442         return Extensible::cull();
443 }
444
445 CullResult LocalUser::cull()
446 {
447         // The iterator is initialized to local_users.end() in the constructor. It is
448         // overwritten in UserManager::AddUser() with the real iterator so this check
449         // is only a precaution currently.
450         if (localuseriter != ServerInstance->Users->local_users.end())
451         {
452                 ServerInstance->Users->local_count--;
453                 ServerInstance->Users->local_users.erase(localuseriter);
454         }
455         else
456                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: LocalUserIter does not point to a valid entry for " + this->nick);
457
458         ClearInvites();
459         eh.cull();
460         return User::cull();
461 }
462
463 CullResult FakeUser::cull()
464 {
465         // Fake users don't quit, they just get culled.
466         quitting = true;
467         ServerInstance->Users->clientlist->erase(nick);
468         ServerInstance->Users->uuidlist->erase(uuid);
469         return User::cull();
470 }
471
472 void User::Oper(OperInfo* info)
473 {
474         if (this->IsModeSet('o'))
475                 this->UnOper();
476
477         this->modes[UM_OPERATOR] = 1;
478         this->oper = info;
479         this->WriteServ("MODE %s :+o", this->nick.c_str());
480         FOREACH_MOD(I_OnOper, OnOper(this, info->name));
481
482         std::string opername;
483         if (info->oper_block)
484                 opername = info->oper_block->getString("name");
485
486         if (IS_LOCAL(this))
487         {
488                 LocalUser* l = IS_LOCAL(this);
489                 std::string vhost = oper->getConfig("vhost");
490                 if (!vhost.empty())
491                         l->ChangeDisplayedHost(vhost.c_str());
492                 std::string opClass = oper->getConfig("class");
493                 if (!opClass.empty())
494                         l->SetClass(opClass);
495         }
496
497         ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",
498                 nick.c_str(), ident.c_str(), host.c_str(), oper->name.c_str(), opername.c_str());
499         this->WriteNumeric(381, "%s :You are now %s %s", nick.c_str(), strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str());
500
501         ServerInstance->Logs->Log("OPER", LOG_DEFAULT, "%s opered as type: %s", GetFullRealHost().c_str(), oper->name.c_str());
502         ServerInstance->Users->all_opers.push_back(this);
503
504         // Expand permissions from config for faster lookup
505         if (IS_LOCAL(this))
506                 oper->init();
507
508         FOREACH_MOD(I_OnPostOper,OnPostOper(this, oper->name, opername));
509 }
510
511 void OperInfo::init()
512 {
513         AllowedOperCommands.clear();
514         AllowedPrivs.clear();
515         AllowedUserModes.reset();
516         AllowedChanModes.reset();
517         AllowedUserModes['o' - 'A'] = true; // Call me paranoid if you want.
518
519         for(std::vector<reference<ConfigTag> >::iterator iter = class_blocks.begin(); iter != class_blocks.end(); ++iter)
520         {
521                 ConfigTag* tag = *iter;
522                 std::string mycmd, mypriv;
523                 /* Process commands */
524                 irc::spacesepstream CommandList(tag->getString("commands"));
525                 while (CommandList.GetToken(mycmd))
526                 {
527                         AllowedOperCommands.insert(mycmd);
528                 }
529
530                 irc::spacesepstream PrivList(tag->getString("privs"));
531                 while (PrivList.GetToken(mypriv))
532                 {
533                         AllowedPrivs.insert(mypriv);
534                 }
535
536                 std::string modes = tag->getString("usermodes");
537                 for (std::string::const_iterator c = modes.begin(); c != modes.end(); ++c)
538                 {
539                         if (*c == '*')
540                         {
541                                 this->AllowedUserModes.set();
542                         }
543                         else if (*c >= 'A' && *c < 'z')
544                         {
545                                 this->AllowedUserModes[*c - 'A'] = true;
546                         }
547                 }
548
549                 modes = tag->getString("chanmodes");
550                 for (std::string::const_iterator c = modes.begin(); c != modes.end(); ++c)
551                 {
552                         if (*c == '*')
553                         {
554                                 this->AllowedChanModes.set();
555                         }
556                         else if (*c >= 'A' && *c < 'z')
557                         {
558                                 this->AllowedChanModes[*c - 'A'] = true;
559                         }
560                 }
561         }
562 }
563
564 void User::UnOper()
565 {
566         if (!this->IsOper())
567                 return;
568
569         /*
570          * unset their oper type (what IS_OPER checks).
571          * note, order is important - this must come before modes as -o attempts
572          * to call UnOper. -- w00t
573          */
574         oper = NULL;
575
576
577         /* Remove all oper only modes from the user when the deoper - Bug #466*/
578         std::string moderemove("-");
579
580         for (unsigned char letter = 'A'; letter <= 'z'; letter++)
581         {
582                 ModeHandler* mh = ServerInstance->Modes->FindMode(letter, MODETYPE_USER);
583                 if (mh && mh->NeedsOper())
584                         moderemove += letter;
585         }
586
587
588         std::vector<std::string> parameters;
589         parameters.push_back(this->nick);
590         parameters.push_back(moderemove);
591
592         ServerInstance->Parser->CallHandler("MODE", parameters, this);
593
594         /* remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404 */
595         ServerInstance->Users->all_opers.remove(this);
596
597         this->modes[UM_OPERATOR] = 0;
598 }
599
600 /*
601  * Check class restrictions
602  */
603 void LocalUser::CheckClass()
604 {
605         ConnectClass* a = this->MyClass;
606
607         if (!a)
608         {
609                 ServerInstance->Users->QuitUser(this, "Access denied by configuration");
610                 return;
611         }
612         else if (a->type == CC_DENY)
613         {
614                 ServerInstance->Users->QuitUser(this, a->config->getString("reason", "Unauthorised connection"));
615                 return;
616         }
617         else if ((a->GetMaxLocal()) && (ServerInstance->Users->LocalCloneCount(this) > a->GetMaxLocal()))
618         {
619                 ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
620                 if (a->maxconnwarn)
621                         ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString().c_str());
622                 return;
623         }
624         else if ((a->GetMaxGlobal()) && (ServerInstance->Users->GlobalCloneCount(this) > a->GetMaxGlobal()))
625         {
626                 ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
627                 if (a->maxconnwarn)
628                         ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString().c_str());
629                 return;
630         }
631
632         this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
633 }
634
635 bool LocalUser::CheckLines(bool doZline)
636 {
637         const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
638
639         if (!this->exempt)
640         {
641                 for (int n = 0; check[n]; ++n)
642                 {
643                         XLine *r = ServerInstance->XLines->MatchesLine(check[n], this);
644
645                         if (r)
646                         {
647                                 r->Apply(this);
648                                 return true;
649                         }
650                 }
651         }
652
653         return false;
654 }
655
656 void LocalUser::FullConnect()
657 {
658         ServerInstance->stats->statsConnects++;
659         this->idle_lastmsg = ServerInstance->Time();
660
661         /*
662          * You may be thinking "wtf, we checked this in User::AddClient!" - and yes, we did, BUT.
663          * At the time AddClient is called, we don't have a resolved host, by here we probably do - which
664          * may put the user into a totally seperate class with different restrictions! so we *must* check again.
665          * Don't remove this! -- w00t
666          */
667         MyClass = NULL;
668         SetClass();
669         CheckClass();
670         CheckLines();
671
672         if (quitting)
673                 return;
674
675         this->WriteNumeric(RPL_WELCOME, "%s :Welcome to the %s IRC Network %s",this->nick.c_str(), ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str());
676         this->WriteNumeric(RPL_YOURHOSTIS, "%s :Your host is %s, running version %s",this->nick.c_str(),ServerInstance->Config->ServerName.c_str(),BRANCH);
677         this->WriteNumeric(RPL_SERVERCREATED, "%s :This server was created %s %s", this->nick.c_str(), __TIME__, __DATE__);
678
679         const std::string& modelist = ServerInstance->Modes->GetModeListFor004Numeric();
680         this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s %s", this->nick.c_str(), ServerInstance->Config->ServerName.c_str(), BRANCH, modelist.c_str());
681
682         ServerInstance->ISupport.SendTo(this);
683         this->WriteNumeric(RPL_YOURUUID, "%s %s :your unique ID", this->nick.c_str(), this->uuid.c_str());
684
685         /* Now registered */
686         if (ServerInstance->Users->unregistered_count)
687                 ServerInstance->Users->unregistered_count--;
688
689         /* Trigger MOTD and LUSERS output, give modules a chance too */
690         ModResult MOD_RESULT;
691         std::string command("LUSERS");
692         std::vector<std::string> parameters;
693         FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
694         if (!MOD_RESULT)
695                 ServerInstance->Parser->CallHandler(command, parameters, this);
696
697         MOD_RESULT = MOD_RES_PASSTHRU;
698         command = "MOTD";
699         FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command));
700         if (!MOD_RESULT)
701                 ServerInstance->Parser->CallHandler(command, parameters, this);
702
703         if (ServerInstance->Config->RawLog)
704                 WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str());
705
706         /*
707          * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
708          * for a user that doesn't exist yet.
709          */
710         FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
711
712         this->registered = REG_ALL;
713
714         FOREACH_MOD(I_OnPostConnect,OnPostConnect(this));
715
716         ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]",
717                 this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->fullname.c_str());
718         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
719         ServerInstance->BanCache->AddHit(this->GetIPString(), "", "");
720         // reset the flood penalty (which could have been raised due to things like auto +x)
721         CommandFloodPenalty = 0;
722 }
723
724 void User::InvalidateCache()
725 {
726         /* Invalidate cache */
727         cached_fullhost.clear();
728         cached_hostip.clear();
729         cached_makehost.clear();
730         cached_fullrealhost.clear();
731 }
732
733 bool User::ChangeNick(const std::string& newnick, bool force)
734 {
735         if (quitting)
736         {
737                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
738                 return false;
739         }
740
741         if (!force)
742         {
743                 ModResult MOD_RESULT;
744                 FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick));
745
746                 if (MOD_RESULT == MOD_RES_DENY)
747                 {
748                         ServerInstance->stats->statsCollisions++;
749                         return false;
750                 }
751         }
752
753         if (assign(newnick) == assign(nick))
754         {
755                 // case change, don't need to check Q:lines and such
756                 // and, if it's identical including case, we can leave right now
757                 if (newnick == nick)
758                         return true;
759         }
760         else
761         {
762                 /*
763                  * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some fucking *moron*
764                  * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be.
765                  * Thanks Kein for finding this. -- w00t
766                  *
767                  * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves.
768                  *              -- w00t
769                  */
770                 if (IS_LOCAL(this) && !force)
771                 {
772                         XLine* mq = ServerInstance->XLines->MatchesLine("Q",newnick);
773                         if (mq)
774                         {
775                                 if (this->registered == REG_ALL)
776                                 {
777                                         ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s",
778                                                 newnick.c_str(), GetFullRealHost().c_str(), mq->reason.c_str());
779                                 }
780                                 this->WriteNumeric(432, "%s %s :Invalid nickname: %s",this->nick.c_str(), newnick.c_str(), mq->reason.c_str());
781                                 return false;
782                         }
783
784                         if (ServerInstance->Config->RestrictBannedUsers)
785                         {
786                                 for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
787                                 {
788                                         Channel *chan = *i;
789                                         if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this))
790                                         {
791                                                 this->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", this->nick.c_str(), chan->name.c_str());
792                                                 return false;
793                                         }
794                                 }
795                         }
796                 }
797
798                 /*
799                  * Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) --
800                  * then we have a potential collide. Check whether someone else is camping on the nick
801                  * (i.e. connect -> send NICK, don't send USER.) If they are camping, force-change the
802                  * camper to their UID, and allow the incoming nick change.
803                  *
804                  * If the guy using the nick is already using it, tell the incoming nick change to gtfo,
805                  * because the nick is already (rightfully) in use. -- w00t
806                  */
807                 User* InUse = ServerInstance->FindNickOnly(newnick);
808                 if (InUse && (InUse != this))
809                 {
810                         if (InUse->registered != REG_ALL)
811                         {
812                                 /* force the camper to their UUID, and ask them to re-send a NICK. */
813                                 InUse->WriteTo(InUse, "NICK %s", InUse->uuid.c_str());
814                                 InUse->WriteNumeric(433, "%s %s :Nickname overruled.", InUse->nick.c_str(), InUse->nick.c_str());
815
816                                 ServerInstance->Users->clientlist->erase(InUse->nick);
817                                 (*(ServerInstance->Users->clientlist))[InUse->uuid] = InUse;
818
819                                 InUse->nick = InUse->uuid;
820                                 InUse->InvalidateCache();
821                                 InUse->registered &= ~REG_NICK;
822                         }
823                         else
824                         {
825                                 /* No camping, tell the incoming user  to stop trying to change nick ;p */
826                                 this->WriteNumeric(433, "%s %s :Nickname is already in use.", this->registered >= REG_NICK ? this->nick.c_str() : "*", newnick.c_str());
827                                 return false;
828                         }
829                 }
830         }
831
832         if (this->registered == REG_ALL)
833                 this->WriteCommon("NICK %s",newnick.c_str());
834         std::string oldnick = nick;
835         nick = newnick;
836
837         InvalidateCache();
838         ServerInstance->Users->clientlist->erase(oldnick);
839         (*(ServerInstance->Users->clientlist))[newnick] = this;
840
841         if (registered == REG_ALL)
842                 FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(this,oldnick));
843
844         return true;
845 }
846
847 int LocalUser::GetServerPort()
848 {
849         switch (this->server_sa.sa.sa_family)
850         {
851                 case AF_INET6:
852                         return htons(this->server_sa.in6.sin6_port);
853                 case AF_INET:
854                         return htons(this->server_sa.in4.sin_port);
855         }
856         return 0;
857 }
858
859 const std::string& User::GetIPString()
860 {
861         int port;
862         if (cachedip.empty())
863         {
864                 irc::sockets::satoap(client_sa, cachedip, port);
865                 /* IP addresses starting with a : on irc are a Bad Thing (tm) */
866                 if (cachedip[0] == ':')
867                         cachedip.insert(0,1,'0');
868         }
869
870         return cachedip;
871 }
872
873 irc::sockets::cidr_mask User::GetCIDRMask()
874 {
875         int range = 0;
876         switch (client_sa.sa.sa_family)
877         {
878                 case AF_INET6:
879                         range = ServerInstance->Config->c_ipv6_range;
880                         break;
881                 case AF_INET:
882                         range = ServerInstance->Config->c_ipv4_range;
883                         break;
884         }
885         return irc::sockets::cidr_mask(client_sa, range);
886 }
887
888 bool User::SetClientIP(const char* sip, bool recheck_eline)
889 {
890         cachedip.clear();
891         cached_hostip.clear();
892         return irc::sockets::aptosa(sip, 0, client_sa);
893 }
894
895 void User::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
896 {
897         cachedip.clear();
898         cached_hostip.clear();
899         memcpy(&client_sa, &sa, sizeof(irc::sockets::sockaddrs));
900 }
901
902 bool LocalUser::SetClientIP(const char* sip, bool recheck_eline)
903 {
904         irc::sockets::sockaddrs sa;
905         if (!irc::sockets::aptosa(sip, 0, sa))
906                 // Invalid
907                 return false;
908
909         LocalUser::SetClientIP(sa, recheck_eline);
910         return true;
911 }
912
913 void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa, bool recheck_eline)
914 {
915         if (sa != client_sa)
916         {
917                 User::SetClientIP(sa);
918                 if (recheck_eline)
919                         this->exempt = (ServerInstance->XLines->MatchesLine("E", this) != NULL);
920
921                 FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(this));
922         }
923 }
924
925 static std::string wide_newline("\r\n");
926
927 void User::Write(const std::string& text)
928 {
929 }
930
931 void User::Write(const char *text, ...)
932 {
933 }
934
935 void LocalUser::Write(const std::string& text)
936 {
937         if (!ServerInstance->SE->BoundsCheckFd(&eh))
938                 return;
939
940         if (text.length() > ServerInstance->Config->Limits.MaxLine - 2)
941         {
942                 // this should happen rarely or never. Crop the string at 512 and try again.
943                 std::string try_again = text.substr(0, ServerInstance->Config->Limits.MaxLine - 2);
944                 Write(try_again);
945                 return;
946         }
947
948         ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %s", uuid.c_str(), text.c_str());
949
950         eh.AddWriteBuf(text);
951         eh.AddWriteBuf(wide_newline);
952
953         ServerInstance->stats->statsSent += text.length() + 2;
954         this->bytes_out += text.length() + 2;
955         this->cmds_out++;
956 }
957
958 /** Write()
959  */
960 void LocalUser::Write(const char *text, ...)
961 {
962         std::string textbuffer;
963         VAFORMAT(textbuffer, text, text);
964         this->Write(textbuffer);
965 }
966
967 void User::WriteServ(const std::string& text)
968 {
969         this->Write(":%s %s",ServerInstance->Config->ServerName.c_str(),text.c_str());
970 }
971
972 /** WriteServ()
973  *  Same as Write(), except `text' is prefixed with `:server.name '.
974  */
975 void User::WriteServ(const char* text, ...)
976 {
977         std::string textbuffer;
978         VAFORMAT(textbuffer, text, text);
979         this->WriteServ(textbuffer);
980 }
981
982 void User::WriteNotice(const std::string& text)
983 {
984         this->WriteServ("NOTICE " + (this->registered == REG_ALL ? this->nick : "*") + " :" + text);
985 }
986
987 void User::WriteNumeric(unsigned int numeric, const char* text, ...)
988 {
989         std::string textbuffer;
990         VAFORMAT(textbuffer, text, text);
991         this->WriteNumeric(numeric, textbuffer);
992 }
993
994 void User::WriteNumeric(unsigned int numeric, const std::string &text)
995 {
996         ModResult MOD_RESULT;
997
998         FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text));
999
1000         if (MOD_RESULT == MOD_RES_DENY)
1001                 return;
1002         
1003         const std::string message = InspIRCd::Format(":%s %03u %s", ServerInstance->Config->ServerName.c_str(),
1004                 numeric, text.c_str());
1005         this->Write(message);
1006 }
1007
1008 void User::WriteFrom(User *user, const std::string &text)
1009 {
1010         const std::string message = ":" + user->GetFullHost() + " " + text;
1011         this->Write(message);
1012 }
1013
1014
1015 /* write text from an originating user to originating user */
1016
1017 void User::WriteFrom(User *user, const char* text, ...)
1018 {
1019         std::string textbuffer;
1020         VAFORMAT(textbuffer, text, text);
1021         this->WriteFrom(user, textbuffer);
1022 }
1023
1024
1025 /* write text to an destination user from a source user (e.g. user privmsg) */
1026
1027 void User::WriteTo(User *dest, const char *data, ...)
1028 {
1029         std::string textbuffer;
1030         VAFORMAT(textbuffer, data, data);
1031         this->WriteTo(dest, textbuffer);
1032 }
1033
1034 void User::WriteTo(User *dest, const std::string &data)
1035 {
1036         dest->WriteFrom(this, data);
1037 }
1038
1039 void User::WriteCommon(const char* text, ...)
1040 {
1041         if (this->registered != REG_ALL || quitting)
1042                 return;
1043
1044         std::string textbuffer;
1045         VAFORMAT(textbuffer, text, text);
1046         textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
1047         this->WriteCommonRaw(textbuffer, true);
1048 }
1049
1050 void User::WriteCommonExcept(const char* text, ...)
1051 {
1052         if (this->registered != REG_ALL || quitting)
1053                 return;
1054
1055         std::string textbuffer;
1056         VAFORMAT(textbuffer, text, text);
1057         textbuffer = ":" + this->GetFullHost() + " " + textbuffer;
1058         this->WriteCommonRaw(textbuffer, false);
1059 }
1060
1061 void User::WriteCommonRaw(const std::string &line, bool include_self)
1062 {
1063         if (this->registered != REG_ALL || quitting)
1064                 return;
1065
1066         LocalUser::already_sent_id++;
1067
1068         UserChanList include_c(chans);
1069         std::map<User*,bool> exceptions;
1070
1071         exceptions[this] = include_self;
1072
1073         FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
1074
1075         for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
1076         {
1077                 LocalUser* u = IS_LOCAL(i->first);
1078                 if (u && !u->quitting)
1079                 {
1080                         u->already_sent = LocalUser::already_sent_id;
1081                         if (i->second)
1082                                 u->Write(line);
1083                 }
1084         }
1085         for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
1086         {
1087                 Channel* c = *v;
1088                 const UserMembList* ulist = c->GetUsers();
1089                 for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
1090                 {
1091                         LocalUser* u = IS_LOCAL(i->first);
1092                         if (u && !u->quitting && u->already_sent != LocalUser::already_sent_id)
1093                         {
1094                                 u->already_sent = LocalUser::already_sent_id;
1095                                 u->Write(line);
1096                         }
1097                 }
1098         }
1099 }
1100
1101 void User::WriteCommonQuit(const std::string &normal_text, const std::string &oper_text)
1102 {
1103         if (this->registered != REG_ALL)
1104                 return;
1105
1106         already_sent_t uniq_id = ++LocalUser::already_sent_id;
1107
1108         const std::string normalMessage = ":" + this->GetFullHost() + " QUIT :" + normal_text;
1109         const std::string operMessage = ":" + this->GetFullHost() + " QUIT :" + oper_text;
1110
1111         UserChanList include_c(chans);
1112         std::map<User*,bool> exceptions;
1113
1114         FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
1115
1116         for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
1117         {
1118                 LocalUser* u = IS_LOCAL(i->first);
1119                 if (u && !u->quitting)
1120                 {
1121                         u->already_sent = uniq_id;
1122                         if (i->second)
1123                                 u->Write(u->IsOper() ? operMessage : normalMessage);
1124                 }
1125         }
1126         for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
1127         {
1128                 const UserMembList* ulist = (*v)->GetUsers();
1129                 for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
1130                 {
1131                         LocalUser* u = IS_LOCAL(i->first);
1132                         if (u && !u->quitting && (u->already_sent != uniq_id))
1133                         {
1134                                 u->already_sent = uniq_id;
1135                                 u->Write(u->IsOper() ? operMessage : normalMessage);
1136                         }
1137                 }
1138         }
1139 }
1140
1141 void LocalUser::SendText(const std::string& line)
1142 {
1143         Write(line);
1144 }
1145
1146 void RemoteUser::SendText(const std::string& line)
1147 {
1148         ServerInstance->PI->PushToClient(this, line);
1149 }
1150
1151 void FakeUser::SendText(const std::string& line)
1152 {
1153 }
1154
1155 void User::SendText(const char *text, ...)
1156 {
1157         std::string line;
1158         VAFORMAT(line, text, text);
1159         SendText(line);
1160 }
1161
1162 void User::SendText(const std::string& linePrefix, std::stringstream& textStream)
1163 {
1164         std::string line;
1165         std::string word;
1166         while (textStream >> word)
1167         {
1168                 size_t lineLength = linePrefix.length() + line.length() + word.length() + 3; // "\s\n\r"
1169                 if (lineLength > ServerInstance->Config->Limits.MaxLine)
1170                 {
1171                         SendText(linePrefix + line);
1172                         line.clear();
1173                 }
1174                 line += " " + word;
1175         }
1176         SendText(linePrefix + line);
1177 }
1178
1179 /* return 0 or 1 depending if users u and u2 share one or more common channels
1180  * (used by QUIT, NICK etc which arent channel specific notices)
1181  *
1182  * The old algorithm in 1.0 for this was relatively inefficient, iterating over
1183  * the first users channels then the second users channels within the outer loop,
1184  * therefore it was a maximum of x*y iterations (upon returning 0 and checking
1185  * all possible iterations). However this new function instead checks against the
1186  * channel's userlist in the inner loop which is a std::map<User*,User*>
1187  * and saves us time as we already know what pointer value we are after.
1188  * Don't quote me on the maths as i am not a mathematician or computer scientist,
1189  * but i believe this algorithm is now x+(log y) maximum iterations instead.
1190  */
1191 bool User::SharesChannelWith(User *other)
1192 {
1193         if ((!other) || (this->registered != REG_ALL) || (other->registered != REG_ALL))
1194                 return false;
1195
1196         /* Outer loop */
1197         for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++)
1198         {
1199                 /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
1200                  * by replacing it with a map::find which *should* be more efficient
1201                  */
1202                 if ((*i)->HasUser(other))
1203                         return true;
1204         }
1205         return false;
1206 }
1207
1208 bool User::ChangeName(const char* gecos)
1209 {
1210         if (!this->fullname.compare(gecos))
1211                 return true;
1212
1213         if (IS_LOCAL(this))
1214         {
1215                 ModResult MOD_RESULT;
1216                 FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos));
1217                 if (MOD_RESULT == MOD_RES_DENY)
1218                         return false;
1219                 FOREACH_MOD(I_OnChangeName,OnChangeName(this,gecos));
1220         }
1221         this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos);
1222
1223         return true;
1224 }
1225
1226 void User::DoHostCycle(const std::string &quitline)
1227 {
1228         if (!ServerInstance->Config->CycleHosts)
1229                 return;
1230
1231         already_sent_t silent_id = ++LocalUser::already_sent_id;
1232         already_sent_t seen_id = ++LocalUser::already_sent_id;
1233
1234         UserChanList include_c(chans);
1235         std::map<User*,bool> exceptions;
1236
1237         FOREACH_MOD(I_OnBuildNeighborList,OnBuildNeighborList(this, include_c, exceptions));
1238
1239         for (std::map<User*,bool>::iterator i = exceptions.begin(); i != exceptions.end(); ++i)
1240         {
1241                 LocalUser* u = IS_LOCAL(i->first);
1242                 if (u && !u->quitting)
1243                 {
1244                         if (i->second)
1245                         {
1246                                 u->already_sent = seen_id;
1247                                 u->Write(quitline);
1248                         }
1249                         else
1250                         {
1251                                 u->already_sent = silent_id;
1252                         }
1253                 }
1254         }
1255         for (UCListIter v = include_c.begin(); v != include_c.end(); ++v)
1256         {
1257                 Channel* c = *v;
1258                 Membership* memb = c->GetUser(this);
1259                 const std::string joinline = ":" + GetFullHost() + " JOIN " + c->name;
1260                 std::string modeline;
1261
1262                 if (!memb->modes.empty())
1263                 {
1264                         modeline = ":" + (ServerInstance->Config->CycleHostsFromUser ? GetFullHost() : ServerInstance->Config->ServerName)
1265                                 + " MODE " + c->name + " +" + memb->modes;
1266
1267                         for (size_t i = 0; i < memb->modes.length(); i++)
1268                                 modeline.append(" ").append(nick);
1269                 }
1270
1271                 const UserMembList *ulist = c->GetUsers();
1272                 for (UserMembList::const_iterator i = ulist->begin(); i != ulist->end(); i++)
1273                 {
1274                         LocalUser* u = IS_LOCAL(i->first);
1275                         if (u == NULL || u == this)
1276                                 continue;
1277                         if (u->already_sent == silent_id)
1278                                 continue;
1279
1280                         if (u->already_sent != seen_id)
1281                         {
1282                                 u->Write(quitline);
1283                                 u->already_sent = seen_id;
1284                         }
1285                         u->Write(joinline);
1286                         if (!memb->modes.empty())
1287                                 u->Write(modeline);
1288                 }
1289         }
1290 }
1291
1292 bool User::ChangeDisplayedHost(const char* shost)
1293 {
1294         if (dhost == shost)
1295                 return true;
1296
1297         if (IS_LOCAL(this))
1298         {
1299                 ModResult MOD_RESULT;
1300                 FIRST_MOD_RESULT(OnChangeLocalUserHost, MOD_RESULT, (IS_LOCAL(this),shost));
1301                 if (MOD_RESULT == MOD_RES_DENY)
1302                         return false;
1303         }
1304
1305         FOREACH_MOD(I_OnChangeHost, OnChangeHost(this,shost));
1306
1307         std::string quitstr = ":" + GetFullHost() + " QUIT :Changing host";
1308
1309         /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */
1310         this->dhost.assign(shost, 0, 64);
1311
1312         this->InvalidateCache();
1313
1314         this->DoHostCycle(quitstr);
1315
1316         if (IS_LOCAL(this))
1317                 this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s %s :is now your displayed host",this->nick.c_str(),this->dhost.c_str());
1318
1319         return true;
1320 }
1321
1322 bool User::ChangeIdent(const char* newident)
1323 {
1324         if (this->ident == newident)
1325                 return true;
1326
1327         FOREACH_MOD(I_OnChangeIdent, OnChangeIdent(this,newident));
1328
1329         std::string quitstr = ":" + GetFullHost() + " QUIT :Changing ident";
1330
1331         this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
1332
1333         this->InvalidateCache();
1334
1335         this->DoHostCycle(quitstr);
1336
1337         return true;
1338 }
1339
1340 void User::SendAll(const char* command, const char* text, ...)
1341 {
1342         std::string textbuffer;
1343         VAFORMAT(textbuffer, text, text);
1344         const std::string message = ":" + this->GetFullHost() + " " + command + " $* :" + textbuffer;
1345
1346         for (LocalUserList::const_iterator i = ServerInstance->Users->local_users.begin(); i != ServerInstance->Users->local_users.end(); i++)
1347         {
1348                 if ((*i)->registered == REG_ALL)
1349                         (*i)->Write(message);
1350         }
1351 }
1352
1353 /*
1354  * Sets a user's connection class.
1355  * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc.
1356  * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
1357  * then their ip will be taken as 'priority' anyway, so for example,
1358  * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
1359  */
1360 void LocalUser::SetClass(const std::string &explicit_name)
1361 {
1362         ConnectClass *found = NULL;
1363
1364         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
1365
1366         if (!explicit_name.empty())
1367         {
1368                 for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
1369                 {
1370                         ConnectClass* c = *i;
1371
1372                         if (explicit_name == c->name)
1373                         {
1374                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Explicitly set to %s", explicit_name.c_str());
1375                                 found = c;
1376                         }
1377                 }
1378         }
1379         else
1380         {
1381                 for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
1382                 {
1383                         ConnectClass* c = *i;
1384                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str());
1385
1386                         ModResult MOD_RESULT;
1387                         FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this,c));
1388                         if (MOD_RESULT == MOD_RES_DENY)
1389                                 continue;
1390                         if (MOD_RESULT == MOD_RES_ALLOW)
1391                         {
1392                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str());
1393                                 found = c;
1394                                 break;
1395                         }
1396
1397                         if (c->type == CC_NAMED)
1398                                 continue;
1399
1400                         bool regdone = (registered != REG_NONE);
1401                         if (c->config->getBool("registered", regdone) != regdone)
1402                                 continue;
1403
1404                         /* check if host matches.. */
1405                         if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
1406                             !InspIRCd::MatchCIDR(this->host, c->GetHost(), NULL))
1407                         {
1408                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "No host match (for %s)", c->GetHost().c_str());
1409                                 continue;
1410                         }
1411
1412                         /*
1413                          * deny change if change will take class over the limit check it HERE, not after we found a matching class,
1414                          * because we should attempt to find another class if this one doesn't match us. -- w00t
1415                          */
1416                         if (c->limit && (c->GetReferenceCount() >= c->limit))
1417                         {
1418                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
1419                                 continue;
1420                         }
1421
1422                         /* if it requires a port ... */
1423                         int port = c->config->getInt("port");
1424                         if (port)
1425                         {
1426                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires port (%d)", port);
1427
1428                                 /* and our port doesn't match, fail. */
1429                                 if (this->GetServerPort() != port)
1430                                         continue;
1431                         }
1432
1433                         if (regdone && !c->config->getString("password").empty())
1434                         {
1435                                 if (ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
1436                                 {
1437                                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Bad password, skipping");
1438                                         continue;
1439                                 }
1440                         }
1441
1442                         /* we stop at the first class that meets ALL critera. */
1443                         found = c;
1444                         break;
1445                 }
1446         }
1447
1448         /*
1449          * Okay, assuming we found a class that matches.. switch us into that class, keeping refcounts up to date.
1450          */
1451         if (found)
1452         {
1453                 MyClass = found;
1454         }
1455 }
1456
1457 /* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
1458  * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
1459  * then their ip will be taken as 'priority' anyway, so for example,
1460  * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
1461  */
1462 ConnectClass* LocalUser::GetClass()
1463 {
1464         return MyClass;
1465 }
1466
1467 ConnectClass* User::GetClass()
1468 {
1469         return NULL;
1470 }
1471
1472 void User::PurgeEmptyChannels()
1473 {
1474         // firstly decrement the count on each channel
1475         for (UCListIter f = this->chans.begin(); f != this->chans.end(); f++)
1476         {
1477                 Channel* c = *f;
1478                 c->DelUser(this);
1479         }
1480
1481         this->UnOper();
1482 }
1483
1484 const std::string& FakeUser::GetFullHost()
1485 {
1486         if (!ServerInstance->Config->HideWhoisServer.empty())
1487                 return ServerInstance->Config->HideWhoisServer;
1488         return server;
1489 }
1490
1491 const std::string& FakeUser::GetFullRealHost()
1492 {
1493         if (!ServerInstance->Config->HideWhoisServer.empty())
1494                 return ServerInstance->Config->HideWhoisServer;
1495         return server;
1496 }
1497
1498 ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask)
1499         : config(tag), type(t), fakelag(true), name("unnamed"), registration_timeout(0), host(mask),
1500         pingtime(0), softsendqmax(0), hardsendqmax(0), recvqmax(0),
1501         penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(0),
1502         limit(0), nouserdns(false)
1503 {
1504 }
1505
1506 ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, const ConnectClass& parent)
1507         : config(tag), type(t), fakelag(parent.fakelag), name("unnamed"),
1508         registration_timeout(parent.registration_timeout), host(mask), pingtime(parent.pingtime),
1509         softsendqmax(parent.softsendqmax), hardsendqmax(parent.hardsendqmax), recvqmax(parent.recvqmax),
1510         penaltythreshold(parent.penaltythreshold), commandrate(parent.commandrate),
1511         maxlocal(parent.maxlocal), maxglobal(parent.maxglobal), maxconnwarn(parent.maxconnwarn), maxchans(parent.maxchans),
1512         limit(parent.limit), nouserdns(parent.nouserdns)
1513 {
1514 }
1515
1516 void ConnectClass::Update(const ConnectClass* src)
1517 {
1518         config = src->config;
1519         type = src->type;
1520         fakelag = src->fakelag;
1521         name = src->name;
1522         registration_timeout = src->registration_timeout;
1523         host = src->host;
1524         pingtime = src->pingtime;
1525         softsendqmax = src->softsendqmax;
1526         hardsendqmax = src->hardsendqmax;
1527         recvqmax = src->recvqmax;
1528         penaltythreshold = src->penaltythreshold;
1529         commandrate = src->commandrate;
1530         maxlocal = src->maxlocal;
1531         maxglobal = src->maxglobal;
1532         maxconnwarn = src->maxconnwarn;
1533         maxchans = src->maxchans;
1534         limit = src->limit;
1535         nouserdns = src->nouserdns;
1536 }