]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/users.cpp
56f9f5591342e17c261579f9e3798f6985e6a9da
[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 "xline.h"
28
29 ClientProtocol::MessageList LocalUser::sendmsglist;
30
31 bool User::IsNoticeMaskSet(unsigned char sm)
32 {
33         if (!isalpha(sm))
34                 return false;
35         return (snomasks[sm-65]);
36 }
37
38 bool User::IsModeSet(unsigned char m) const
39 {
40         ModeHandler* mh = ServerInstance->Modes->FindMode(m, MODETYPE_USER);
41         return (mh && modes[mh->GetId()]);
42 }
43
44 std::string User::GetModeLetters(bool includeparams) const
45 {
46         std::string ret(1, '+');
47         std::string params;
48
49         for (unsigned char i = 'A'; i <= 'z'; i++)
50         {
51                 const ModeHandler* const mh = ServerInstance->Modes.FindMode(i, MODETYPE_USER);
52                 if ((!mh) || (!IsModeSet(mh)))
53                         continue;
54
55                 ret.push_back(mh->GetModeChar());
56                 if ((includeparams) && (mh->NeedsParam(true)))
57                 {
58                         const std::string val = mh->GetUserParameter(this);
59                         if (!val.empty())
60                                 params.append(1, ' ').append(val);
61                 }
62         }
63
64         ret += params;
65         return ret;
66 }
67
68 User::User(const std::string& uid, Server* srv, UserType type)
69         : age(ServerInstance->Time())
70         , signon(0)
71         , uuid(uid)
72         , server(srv)
73         , registered(REG_NONE)
74         , quitting(false)
75         , usertype(type)
76 {
77         client_sa.sa.sa_family = AF_UNSPEC;
78
79         ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New UUID for user: %s", uuid.c_str());
80
81         // Do not insert FakeUsers into the uuidlist so FindUUID() won't return them which is the desired behavior
82         if (type != USERTYPE_SERVER)
83         {
84                 if (!ServerInstance->Users.uuidlist.insert(std::make_pair(uuid, this)).second)
85                         throw CoreException("Duplicate UUID in User constructor: " + uuid);
86         }
87 }
88
89 LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* servaddr)
90         : User(ServerInstance->UIDGen.GetUID(), ServerInstance->FakeClient->server, USERTYPE_LOCAL)
91         , eh(this)
92         , serializer(NULL)
93         , bytes_in(0)
94         , bytes_out(0)
95         , cmds_in(0)
96         , cmds_out(0)
97         , quitting_sendq(false)
98         , lastping(true)
99         , exempt(false)
100         , nextping(0)
101         , idle_lastmsg(0)
102         , CommandFloodPenalty(0)
103         , already_sent(0)
104 {
105         signon = ServerInstance->Time();
106         // The user's default nick is their UUID
107         nick = uuid;
108         ident = "unknown";
109         eh.SetFd(myfd);
110         memcpy(&client_sa, client, sizeof(irc::sockets::sockaddrs));
111         memcpy(&server_sa, servaddr, sizeof(irc::sockets::sockaddrs));
112         ChangeRealHost(GetIPString(), true);
113 }
114
115 User::~User()
116 {
117 }
118
119 const std::string& User::MakeHost()
120 {
121         if (!this->cached_makehost.empty())
122                 return this->cached_makehost;
123
124         // XXX: Is there really a need to cache this?
125         this->cached_makehost = ident + "@" + GetRealHost();
126         return this->cached_makehost;
127 }
128
129 const std::string& User::MakeHostIP()
130 {
131         if (!this->cached_hostip.empty())
132                 return this->cached_hostip;
133
134         // XXX: Is there really a need to cache this?
135         this->cached_hostip = ident + "@" + this->GetIPString();
136         return this->cached_hostip;
137 }
138
139 const std::string& User::GetFullHost()
140 {
141         if (!this->cached_fullhost.empty())
142                 return this->cached_fullhost;
143
144         // XXX: Is there really a need to cache this?
145         this->cached_fullhost = nick + "!" + ident + "@" + GetDisplayedHost();
146         return this->cached_fullhost;
147 }
148
149 const std::string& User::GetFullRealHost()
150 {
151         if (!this->cached_fullrealhost.empty())
152                 return this->cached_fullrealhost;
153
154         // XXX: Is there really a need to cache this?
155         this->cached_fullrealhost = nick + "!" + ident + "@" + GetRealHost();
156         return this->cached_fullrealhost;
157 }
158
159 bool User::HasModePermission(const ModeHandler* mh) const
160 {
161         return true;
162 }
163
164 bool LocalUser::HasModePermission(const ModeHandler* mh) const
165 {
166         if (!this->IsOper())
167                 return false;
168
169         const unsigned char mode = mh->GetModeChar();
170         if (!ModeParser::IsModeChar(mode))
171                 return false;
172
173         return ((mh->GetModeType() == MODETYPE_USER ? oper->AllowedUserModes : oper->AllowedChanModes))[(mode - 'A')];
174
175 }
176 /*
177  * users on remote servers can completely bypass all permissions based checks.
178  * This prevents desyncs when one server has different type/class tags to another.
179  * That having been said, this does open things up to the possibility of source changes
180  * allowing remote kills, etc - but if they have access to the src, they most likely have
181  * access to the conf - so it's an end to a means either way.
182  */
183 bool User::HasCommandPermission(const std::string&)
184 {
185         return true;
186 }
187
188 bool LocalUser::HasCommandPermission(const std::string& command)
189 {
190         // are they even an oper at all?
191         if (!this->IsOper())
192         {
193                 return false;
194         }
195
196         return oper->AllowedOperCommands.Contains(command);
197 }
198
199 bool User::HasPrivPermission(const std::string& privstr)
200 {
201         return true;
202 }
203
204 bool LocalUser::HasPrivPermission(const std::string& privstr)
205 {
206         if (!this->IsOper())
207                 return false;
208
209         return oper->AllowedPrivs.Contains(privstr);
210 }
211
212 void UserIOHandler::OnDataReady()
213 {
214         if (user->quitting)
215                 return;
216
217         if (recvq.length() > user->MyClass->GetRecvqMax() && !user->HasPrivPermission("users/flood/increased-buffers"))
218         {
219                 ServerInstance->Users->QuitUser(user, "RecvQ exceeded");
220                 ServerInstance->SNO->WriteToSnoMask('a', "User %s RecvQ of %lu exceeds connect class maximum of %lu",
221                         user->nick.c_str(), (unsigned long)recvq.length(), user->MyClass->GetRecvqMax());
222                 return;
223         }
224
225         unsigned long sendqmax = ULONG_MAX;
226         if (!user->HasPrivPermission("users/flood/increased-buffers"))
227                 sendqmax = user->MyClass->GetSendqSoftMax();
228
229         unsigned long penaltymax = ULONG_MAX;
230         if (!user->HasPrivPermission("users/flood/no-fakelag"))
231                 penaltymax = user->MyClass->GetPenaltyThreshold() * 1000;
232
233         // The cleaned message sent by the user or empty if not found yet.
234         std::string line;
235
236         // The position of the most \n character or npos if not found yet.
237         std::string::size_type eolpos;
238
239         // The position within the recvq of the current character.
240         std::string::size_type qpos;
241
242         while (user->CommandFloodPenalty < penaltymax && getSendQSize() < sendqmax)
243         {
244                 // Check the newly received data for an EOL.
245                 eolpos = recvq.find('\n', checked_until);
246                 if (eolpos == std::string::npos)
247                 {
248                         checked_until = recvq.length();
249                         return;
250                 }
251
252                 // We've found a line! Clean it up and move it to the line buffer.
253                 line.reserve(eolpos);
254                 for (qpos = 0; qpos < eolpos; ++qpos)
255                 {
256                         char c = recvq[qpos];
257                         switch (c)
258                         {
259                                 case '\0':
260                                         c = ' ';
261                                         break;
262                                 case '\r':
263                                         continue;
264                         }
265
266                         line.push_back(c);
267                 }
268
269                 // just found a newline. Terminate the string, and pull it out of recvq
270                 recvq.erase(0, eolpos + 1);
271                 checked_until = 0;
272
273                 // TODO should this be moved to when it was inserted in recvq?
274                 ServerInstance->stats.Recv += qpos;
275                 user->bytes_in += qpos;
276                 user->cmds_in++;
277
278                 ServerInstance->Parser.ProcessBuffer(user, line);
279                 if (user->quitting)
280                         return;
281
282                 // clear() does not reclaim memory associated with the string, so our .reserve() call is safe
283                 line.clear();
284         }
285
286         if (user->CommandFloodPenalty >= penaltymax && !user->MyClass->fakelag)
287                 ServerInstance->Users->QuitUser(user, "Excess Flood");
288 }
289
290 void UserIOHandler::AddWriteBuf(const std::string &data)
291 {
292         if (user->quitting_sendq)
293                 return;
294         if (!user->quitting && getSendQSize() + data.length() > user->MyClass->GetSendqHardMax() &&
295                 !user->HasPrivPermission("users/flood/increased-buffers"))
296         {
297                 user->quitting_sendq = true;
298                 ServerInstance->GlobalCulls.AddSQItem(user);
299                 return;
300         }
301
302         // We still want to append data to the sendq of a quitting user,
303         // e.g. their ERROR message that says 'closing link'
304
305         WriteData(data);
306 }
307
308 bool UserIOHandler::OnSetEndPoint(const irc::sockets::sockaddrs& server, const irc::sockets::sockaddrs& client)
309 {
310         memcpy(&user->server_sa, &server, sizeof(irc::sockets::sockaddrs));
311         user->SetClientIP(client);
312         return !user->quitting;
313 }
314
315 void UserIOHandler::OnError(BufferedSocketError)
316 {
317         ServerInstance->Users->QuitUser(user, getError());
318 }
319
320 CullResult User::cull()
321 {
322         if (!quitting)
323                 ServerInstance->Users->QuitUser(this, "Culled without QuitUser");
324
325         if (client_sa.family() != AF_UNSPEC)
326                 ServerInstance->Users->RemoveCloneCounts(this);
327
328         return Extensible::cull();
329 }
330
331 CullResult LocalUser::cull()
332 {
333         eh.cull();
334         return User::cull();
335 }
336
337 CullResult FakeUser::cull()
338 {
339         // Fake users don't quit, they just get culled.
340         quitting = true;
341         // Fake users are not inserted into UserManager::clientlist or uuidlist, so we don't need to modify those here
342         return User::cull();
343 }
344
345 void User::Oper(OperInfo* info)
346 {
347         ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
348         if (opermh)
349         {
350                 if (this->IsModeSet(opermh))
351                         this->UnOper();
352                 this->SetMode(opermh, true);
353         }
354         this->oper = info;
355
356         LocalUser* localuser = IS_LOCAL(this);
357         if (localuser)
358         {
359                 Modes::ChangeList changelist;
360                 changelist.push_add(opermh);
361                 ClientProtocol::Events::Mode modemsg(ServerInstance->FakeClient, NULL, localuser, changelist);
362                 localuser->Send(modemsg);
363         }
364
365         FOREACH_MOD(OnOper, (this, info->name));
366
367         std::string opername;
368         if (info->oper_block)
369                 opername = info->oper_block->getString("name");
370
371         ServerInstance->SNO->WriteToSnoMask('o',"%s (%s@%s) is now an IRC operator of type %s (using oper '%s')",
372                 nick.c_str(), ident.c_str(), GetRealHost().c_str(), oper->name.c_str(), opername.c_str());
373         this->WriteNumeric(RPL_YOUAREOPER, InspIRCd::Format("You are now %s %s", strchr("aeiouAEIOU", oper->name[0]) ? "an" : "a", oper->name.c_str()));
374
375         ServerInstance->Users->all_opers.push_back(this);
376
377         // Expand permissions from config for faster lookup
378         if (localuser)
379                 oper->init();
380
381         FOREACH_MOD(OnPostOper, (this, oper->name, opername));
382 }
383
384 void OperInfo::init()
385 {
386         AllowedOperCommands.Clear();
387         AllowedPrivs.Clear();
388         AllowedUserModes.reset();
389         AllowedChanModes.reset();
390         AllowedUserModes['o' - 'A'] = true; // Call me paranoid if you want.
391
392         for(std::vector<reference<ConfigTag> >::iterator iter = class_blocks.begin(); iter != class_blocks.end(); ++iter)
393         {
394                 ConfigTag* tag = *iter;
395
396                 AllowedOperCommands.AddList(tag->getString("commands"));
397                 AllowedPrivs.AddList(tag->getString("privs"));
398
399                 std::string modes = tag->getString("usermodes");
400                 for (std::string::const_iterator c = modes.begin(); c != modes.end(); ++c)
401                 {
402                         if (*c == '*')
403                         {
404                                 this->AllowedUserModes.set();
405                         }
406                         else if (*c >= 'A' && *c <= 'z')
407                         {
408                                 this->AllowedUserModes[*c - 'A'] = true;
409                         }
410                 }
411
412                 modes = tag->getString("chanmodes");
413                 for (std::string::const_iterator c = modes.begin(); c != modes.end(); ++c)
414                 {
415                         if (*c == '*')
416                         {
417                                 this->AllowedChanModes.set();
418                         }
419                         else if (*c >= 'A' && *c <= 'z')
420                         {
421                                 this->AllowedChanModes[*c - 'A'] = true;
422                         }
423                 }
424         }
425 }
426
427 void User::UnOper()
428 {
429         if (!this->IsOper())
430                 return;
431
432         /*
433          * unset their oper type (what IS_OPER checks).
434          * note, order is important - this must come before modes as -o attempts
435          * to call UnOper. -- w00t
436          */
437         oper = NULL;
438
439
440         /* Remove all oper only modes from the user when the deoper - Bug #466*/
441         Modes::ChangeList changelist;
442         const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
443         for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); ++i)
444         {
445                 ModeHandler* mh = i->second;
446                 if (mh->NeedsOper())
447                         changelist.push_remove(mh);
448         }
449
450         ServerInstance->Modes->Process(this, NULL, this, changelist);
451
452         // Remove the user from the oper list
453         stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);
454
455         ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
456         if (opermh)
457                 this->SetMode(opermh, false);
458         FOREACH_MOD(OnPostDeoper, (this));
459 }
460
461 /*
462  * Check class restrictions
463  */
464 void LocalUser::CheckClass(bool clone_count)
465 {
466         ConnectClass* a = this->MyClass;
467
468         if (!a)
469         {
470                 ServerInstance->Users->QuitUser(this, "Access denied by configuration");
471                 return;
472         }
473         else if (a->type == CC_DENY)
474         {
475                 ServerInstance->Users->QuitUser(this, a->config->getString("reason", "Unauthorised connection"));
476                 return;
477         }
478         else if (clone_count)
479         {
480                 const UserManager::CloneCounts& clonecounts = ServerInstance->Users->GetCloneCounts(this);
481                 if ((a->GetMaxLocal()) && (clonecounts.local > a->GetMaxLocal()))
482                 {
483                         ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (local)");
484                         if (a->maxconnwarn)
485                                 ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString().c_str());
486                         return;
487                 }
488                 else if ((a->GetMaxGlobal()) && (clonecounts.global > a->GetMaxGlobal()))
489                 {
490                         ServerInstance->Users->QuitUser(this, "No more connections allowed from your host via this connect class (global)");
491                         if (a->maxconnwarn)
492                                 ServerInstance->SNO->WriteToSnoMask('a', "WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString().c_str());
493                         return;
494                 }
495         }
496
497         this->nextping = ServerInstance->Time() + a->GetPingTime();
498 }
499
500 bool LocalUser::CheckLines(bool doZline)
501 {
502         const char* check[] = { "G" , "K", (doZline) ? "Z" : NULL, NULL };
503
504         if (!this->exempt)
505         {
506                 for (int n = 0; check[n]; ++n)
507                 {
508                         XLine *r = ServerInstance->XLines->MatchesLine(check[n], this);
509
510                         if (r)
511                         {
512                                 r->Apply(this);
513                                 return true;
514                         }
515                 }
516         }
517
518         return false;
519 }
520
521 void LocalUser::FullConnect()
522 {
523         ServerInstance->stats.Connects++;
524         this->idle_lastmsg = ServerInstance->Time();
525
526         /*
527          * You may be thinking "wtf, we checked this in User::AddClient!" - and yes, we did, BUT.
528          * At the time AddClient is called, we don't have a resolved host, by here we probably do - which
529          * may put the user into a totally seperate class with different restrictions! so we *must* check again.
530          * Don't remove this! -- w00t
531          */
532         MyClass = NULL;
533         SetClass();
534         CheckClass();
535         CheckLines();
536
537         if (quitting)
538                 return;
539
540         /*
541          * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
542          * for a user that doesn't exist yet.
543          */
544         FOREACH_MOD(OnUserConnect, (this));
545
546         /* Now registered */
547         if (ServerInstance->Users->unregistered_count)
548                 ServerInstance->Users->unregistered_count--;
549         this->registered = REG_ALL;
550
551         FOREACH_MOD(OnPostConnect, (this));
552
553         ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]",
554                 this->server_sa.port(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->GetRealName().c_str());
555         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString());
556         ServerInstance->BanCache.AddHit(this->GetIPString(), "", "");
557         // reset the flood penalty (which could have been raised due to things like auto +x)
558         CommandFloodPenalty = 0;
559 }
560
561 void User::InvalidateCache()
562 {
563         /* Invalidate cache */
564         cachedip.clear();
565         cached_fullhost.clear();
566         cached_hostip.clear();
567         cached_makehost.clear();
568         cached_fullrealhost.clear();
569 }
570
571 bool User::ChangeNick(const std::string& newnick, time_t newts)
572 {
573         if (quitting)
574         {
575                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: " + this->nick);
576                 return false;
577         }
578
579         User* const InUse = ServerInstance->FindNickOnly(newnick);
580         if (InUse == this)
581         {
582                 // case change, don't need to check campers
583                 // and, if it's identical including case, we can leave right now
584                 // We also don't update the nick TS if it's a case change, either
585                 if (newnick == nick)
586                         return true;
587         }
588         else
589         {
590                 /*
591                  * Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) --
592                  * then we have a potential collide. Check whether someone else is camping on the nick
593                  * (i.e. connect -> send NICK, don't send USER.) If they are camping, force-change the
594                  * camper to their UID, and allow the incoming nick change.
595                  *
596                  * If the guy using the nick is already using it, tell the incoming nick change to gtfo,
597                  * because the nick is already (rightfully) in use. -- w00t
598                  */
599                 if (InUse)
600                 {
601                         if (InUse->registered != REG_ALL)
602                         {
603                                 /* force the camper to their UUID, and ask them to re-send a NICK. */
604                                 LocalUser* const localuser = static_cast<LocalUser*>(InUse);
605                                 localuser->OverruleNick();
606                         }
607                         else
608                         {
609                                 /* No camping, tell the incoming user  to stop trying to change nick ;p */
610                                 this->WriteNumeric(ERR_NICKNAMEINUSE, newnick, "Nickname is already in use.");
611                                 return false;
612                         }
613                 }
614
615                 age = newts ? newts : ServerInstance->Time();
616         }
617
618         if (this->registered == REG_ALL)
619         {
620                 ClientProtocol::Messages::Nick nickmsg(this, newnick);
621                 ClientProtocol::Event nickevent(ServerInstance->GetRFCEvents().nick, nickmsg);
622                 this->WriteCommonRaw(nickevent, true);
623         }
624         const std::string oldnick = nick;
625         nick = newnick;
626
627         InvalidateCache();
628         ServerInstance->Users->clientlist.erase(oldnick);
629         ServerInstance->Users->clientlist[newnick] = this;
630
631         if (registered == REG_ALL)
632                 FOREACH_MOD(OnUserPostNick, (this,oldnick));
633
634         return true;
635 }
636
637 void LocalUser::OverruleNick()
638 {
639         {
640                 ClientProtocol::Messages::Nick nickmsg(this, this->uuid);
641                 this->Send(ServerInstance->GetRFCEvents().nick, nickmsg);
642         }
643         this->WriteNumeric(ERR_NICKNAMEINUSE, this->nick, "Nickname overruled.");
644
645         // Clear the bit before calling ChangeNick() to make it NOT run the OnUserPostNick() hook
646         this->registered &= ~REG_NICK;
647         this->ChangeNick(this->uuid);
648 }
649
650 const std::string& User::GetIPString()
651 {
652         if (cachedip.empty())
653         {
654                 cachedip = client_sa.addr();
655                 /* IP addresses starting with a : on irc are a Bad Thing (tm) */
656                 if (cachedip[0] == ':')
657                         cachedip.insert(cachedip.begin(),1,'0');
658         }
659
660         return cachedip;
661 }
662
663 const std::string& User::GetHost(bool uncloak) const
664 {
665         return uncloak ? GetRealHost() : GetDisplayedHost();
666 }
667
668 const std::string& User::GetDisplayedHost() const
669 {
670         return displayhost.empty() ? realhost : displayhost;
671 }
672
673 const std::string& User::GetRealHost() const
674 {
675         return realhost;
676 }
677
678 const std::string& User::GetRealName() const
679 {
680         return realname;
681 }
682
683 irc::sockets::cidr_mask User::GetCIDRMask()
684 {
685         unsigned char range = 0;
686         switch (client_sa.family())
687         {
688                 case AF_INET6:
689                         range = ServerInstance->Config->c_ipv6_range;
690                         break;
691                 case AF_INET:
692                         range = ServerInstance->Config->c_ipv4_range;
693                         break;
694         }
695         return irc::sockets::cidr_mask(client_sa, range);
696 }
697
698 bool User::SetClientIP(const std::string& address)
699 {
700         irc::sockets::sockaddrs sa;
701         if (!irc::sockets::aptosa(address, client_sa.port(), sa))
702                 return false;
703
704         User::SetClientIP(sa);
705         return true;
706 }
707
708 void User::SetClientIP(const irc::sockets::sockaddrs& sa)
709 {
710         const std::string oldip(GetIPString());
711         memcpy(&client_sa, &sa, sizeof(irc::sockets::sockaddrs));
712         this->InvalidateCache();
713
714         // If the users hostname was their IP then update it.
715         if (GetRealHost() == oldip)
716                 ChangeRealHost(GetIPString(), false);
717         if (GetDisplayedHost() == oldip)
718                 ChangeDisplayedHost(GetIPString());
719 }
720
721 bool LocalUser::SetClientIP(const std::string& address)
722 {
723         irc::sockets::sockaddrs sa;
724         if (!irc::sockets::aptosa(address, client_sa.port(), sa))
725                 return false;
726
727         LocalUser::SetClientIP(sa);
728         return true;
729 }
730
731 void LocalUser::SetClientIP(const irc::sockets::sockaddrs& sa)
732 {
733         if (sa == client_sa)
734                 return;
735
736         ServerInstance->Users->RemoveCloneCounts(this);
737
738         User::SetClientIP(sa);
739
740         FOREACH_MOD(OnSetUserIP, (this));
741
742         ServerInstance->Users->AddClone(this);
743
744         // Recheck the connect class.
745         this->MyClass = NULL;
746         this->SetClass();
747         this->CheckClass();
748 }
749
750 void LocalUser::Write(const ClientProtocol::SerializedMessage& text)
751 {
752         if (!SocketEngine::BoundsCheckFd(&eh))
753                 return;
754
755         if (ServerInstance->Config->RawLog)
756         {
757                 if (text.empty())
758                         return;
759
760                 std::string::size_type nlpos = text.find_first_of("\r\n", 0, 2);
761                 if (nlpos == std::string::npos)
762                         nlpos = text.length(); // TODO is this ok, test it
763
764                 ServerInstance->Logs->Log("USEROUTPUT", LOG_RAWIO, "C[%s] O %.*s", uuid.c_str(), (int) nlpos, text.c_str());
765         }
766
767         eh.AddWriteBuf(text);
768
769         const size_t bytessent = text.length() + 2;
770         ServerInstance->stats.Sent += bytessent;
771         this->bytes_out += bytessent;
772         this->cmds_out++;
773 }
774
775 void LocalUser::Send(ClientProtocol::Event& protoev)
776 {
777         if (!serializer)
778         {
779                 ServerInstance->Logs->Log("USERS", LOG_DEBUG, "BUG: LocalUser::Send() called on %s who does not have a serializer!",
780                         GetFullRealHost().c_str());
781                 return;
782         }
783
784         // In the most common case a static LocalUser field, sendmsglist, is passed to the event to be
785         // populated. The list is cleared before returning.
786         // To handle re-enters, if sendmsglist is non-empty upon entering the method then a temporary
787         // list is used instead of the static one.
788         if (sendmsglist.empty())
789         {
790                 Send(protoev, sendmsglist);
791                 sendmsglist.clear();
792         }
793         else
794         {
795                 ClientProtocol::MessageList msglist;
796                 Send(protoev, msglist);
797         }
798 }
799
800 void LocalUser::Send(ClientProtocol::Event& protoev, ClientProtocol::MessageList& msglist)
801 {
802         // Modules can personalize the messages sent per user for the event
803         protoev.GetMessagesForUser(this, msglist);
804         for (ClientProtocol::MessageList::const_iterator i = msglist.begin(); i != msglist.end(); ++i)
805         {
806                 ClientProtocol::Message& curr = **i;
807                 ModResult res;
808                 FIRST_MOD_RESULT(OnUserWrite, res, (this, curr));
809                 if (res != MOD_RES_DENY)
810                         Write(serializer->SerializeForUser(this, curr));
811         }
812 }
813
814 void User::WriteNumeric(const Numeric::Numeric& numeric)
815 {
816         LocalUser* const localuser = IS_LOCAL(this);
817         if (!localuser)
818                 return;
819
820         ModResult MOD_RESULT;
821
822         FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric));
823
824         if (MOD_RESULT == MOD_RES_DENY)
825                 return;
826
827         ClientProtocol::Messages::Numeric numericmsg(numeric, localuser);
828         localuser->Send(ServerInstance->GetRFCEvents().numeric, numericmsg);
829 }
830
831 void User::WriteRemoteNotice(const std::string& text)
832 {
833         ServerInstance->PI->SendUserNotice(this, text);
834 }
835
836 void LocalUser::WriteRemoteNotice(const std::string& text)
837 {
838         WriteNotice(text);
839 }
840
841 namespace
842 {
843         class WriteCommonRawHandler : public User::ForEachNeighborHandler
844         {
845                 ClientProtocol::Event& ev;
846
847                 void Execute(LocalUser* user) CXX11_OVERRIDE
848                 {
849                         user->Send(ev);
850                 }
851
852          public:
853                 WriteCommonRawHandler(ClientProtocol::Event& protoev)
854                         : ev(protoev)
855                 {
856                 }
857         };
858 }
859
860 void User::WriteCommonRaw(ClientProtocol::Event& protoev, bool include_self)
861 {
862         WriteCommonRawHandler handler(protoev);
863         ForEachNeighbor(handler, include_self);
864 }
865
866 void User::ForEachNeighbor(ForEachNeighborHandler& handler, bool include_self)
867 {
868         // The basic logic for visiting the neighbors of a user is to iterate the channel list of the user
869         // and visit all users on those channels. Because two users may share more than one common channel,
870         // we must skip users that we have already visited.
871         // To do this, we make use of a global counter and an integral 'already_sent' field in LocalUser.
872         // The global counter is incremented every time we do something for each neighbor of a user. Then,
873         // before visiting a member we examine user->already_sent. If it's equal to the current counter, we
874         // skip the member. Otherwise, we set it to the current counter and visit the member.
875
876         // Ask modules to build a list of exceptions.
877         // Mods may also exclude entire channels by erasing them from include_chans.
878         IncludeChanList include_chans(chans.begin(), chans.end());
879         std::map<User*, bool> exceptions;
880         exceptions[this] = include_self;
881         FOREACH_MOD(OnBuildNeighborList, (this, include_chans, exceptions));
882
883         // Get next id, guaranteed to differ from the already_sent field of all users
884         const already_sent_t newid = ServerInstance->Users.NextAlreadySentId();
885
886         // Handle exceptions first
887         for (std::map<User*, bool>::const_iterator i = exceptions.begin(); i != exceptions.end(); ++i)
888         {
889                 LocalUser* curr = IS_LOCAL(i->first);
890                 if (curr)
891                 {
892                         // Mark as visited to ensure we won't visit again if there is a common channel
893                         curr->already_sent = newid;
894                         // Always treat quitting users as excluded
895                         if ((i->second) && (!curr->quitting))
896                                 handler.Execute(curr);
897                 }
898         }
899
900         // Now consider the real neighbors
901         for (IncludeChanList::const_iterator i = include_chans.begin(); i != include_chans.end(); ++i)
902         {
903                 Channel* chan = (*i)->chan;
904                 const Channel::MemberMap& userlist = chan->GetUsers();
905                 for (Channel::MemberMap::const_iterator j = userlist.begin(); j != userlist.end(); ++j)
906                 {
907                         LocalUser* curr = IS_LOCAL(j->first);
908                         // User not yet visited?
909                         if ((curr) && (curr->already_sent != newid))
910                         {
911                                 // Mark as visited and execute function
912                                 curr->already_sent = newid;
913                                 handler.Execute(curr);
914                         }
915                 }
916         }
917 }
918
919 void User::WriteRemoteNumeric(const Numeric::Numeric& numeric)
920 {
921         WriteNumeric(numeric);
922 }
923
924 /* return 0 or 1 depending if users u and u2 share one or more common channels
925  * (used by QUIT, NICK etc which arent channel specific notices)
926  *
927  * The old algorithm in 1.0 for this was relatively inefficient, iterating over
928  * the first users channels then the second users channels within the outer loop,
929  * therefore it was a maximum of x*y iterations (upon returning 0 and checking
930  * all possible iterations). However this new function instead checks against the
931  * channel's userlist in the inner loop which is a std::map<User*,User*>
932  * and saves us time as we already know what pointer value we are after.
933  * Don't quote me on the maths as i am not a mathematician or computer scientist,
934  * but i believe this algorithm is now x+(log y) maximum iterations instead.
935  */
936 bool User::SharesChannelWith(User *other)
937 {
938         /* Outer loop */
939         for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); ++i)
940         {
941                 /* Eliminate the inner loop (which used to be ~equal in size to the outer loop)
942                  * by replacing it with a map::find which *should* be more efficient
943                  */
944                 if ((*i)->chan->HasUser(other))
945                         return true;
946         }
947         return false;
948 }
949
950 bool User::ChangeRealName(const std::string& real)
951 {
952         if (!this->realname.compare(real))
953                 return true;
954
955         if (IS_LOCAL(this))
956         {
957                 ModResult MOD_RESULT;
958                 FIRST_MOD_RESULT(OnPreChangeRealName, MOD_RESULT, (IS_LOCAL(this), real));
959                 if (MOD_RESULT == MOD_RES_DENY)
960                         return false;
961                 FOREACH_MOD(OnChangeRealName, (this, real));
962         }
963         this->realname.assign(real, 0, ServerInstance->Config->Limits.MaxReal);
964
965         return true;
966 }
967
968 bool User::ChangeDisplayedHost(const std::string& shost)
969 {
970         if (GetDisplayedHost() == shost)
971                 return true;
972
973         LocalUser* luser = IS_LOCAL(this);
974         if (luser)
975         {
976                 ModResult MOD_RESULT;
977                 FIRST_MOD_RESULT(OnPreChangeHost, MOD_RESULT, (luser, shost));
978                 if (MOD_RESULT == MOD_RES_DENY)
979                         return false;
980         }
981
982         FOREACH_MOD(OnChangeHost, (this,shost));
983
984         if (realhost == shost)
985                 this->displayhost.clear();
986         else
987                 this->displayhost.assign(shost, 0, ServerInstance->Config->Limits.MaxHost);
988
989         this->InvalidateCache();
990
991         if (IS_LOCAL(this) && this->registered != REG_NONE)
992                 this->WriteNumeric(RPL_YOURDISPLAYEDHOST, this->GetDisplayedHost(), "is now your displayed host");
993
994         return true;
995 }
996
997 void User::ChangeRealHost(const std::string& host, bool resetdisplay)
998 {
999         // If the real host is the new host and we are not resetting the
1000         // display host then we have nothing to do.
1001         const bool changehost = (realhost != host);
1002         if (!changehost && !resetdisplay)
1003                 return;
1004
1005         // If the displayhost is not set and we are not resetting it then
1006         // we need to copy it to the displayhost field.
1007         if (displayhost.empty() && !resetdisplay)
1008                 displayhost = realhost;
1009
1010         // If the displayhost is the new host or we are resetting it then
1011         // we clear its contents to save memory.
1012         else if (displayhost == host || resetdisplay)
1013                 displayhost.clear();
1014
1015         // If we are just resetting the display host then we don't need to
1016         // do anything else.
1017         if (!changehost)
1018                 return;
1019
1020         realhost = host;
1021         this->InvalidateCache();
1022 }
1023
1024 bool User::ChangeIdent(const std::string& newident)
1025 {
1026         if (this->ident == newident)
1027                 return true;
1028
1029         FOREACH_MOD(OnChangeIdent, (this,newident));
1030
1031         this->ident.assign(newident, 0, ServerInstance->Config->Limits.IdentMax);
1032         this->InvalidateCache();
1033
1034         return true;
1035 }
1036
1037 /*
1038  * Sets a user's connection class.
1039  * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc.
1040  * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
1041  * then their ip will be taken as 'priority' anyway, so for example,
1042  * <connect allow="127.0.0.1"> will match joe!bloggs@localhost
1043  */
1044 void LocalUser::SetClass(const std::string &explicit_name)
1045 {
1046         ConnectClass *found = NULL;
1047
1048         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Setting connect class for UID %s", this->uuid.c_str());
1049
1050         if (!explicit_name.empty())
1051         {
1052                 for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
1053                 {
1054                         ConnectClass* c = *i;
1055
1056                         if (explicit_name == c->name)
1057                         {
1058                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Explicitly set to %s", explicit_name.c_str());
1059                                 found = c;
1060                         }
1061                 }
1062         }
1063         else
1064         {
1065                 for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i)
1066                 {
1067                         ConnectClass* c = *i;
1068                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str());
1069
1070                         ModResult MOD_RESULT;
1071                         FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this,c));
1072                         if (MOD_RESULT == MOD_RES_DENY)
1073                                 continue;
1074                         if (MOD_RESULT == MOD_RES_ALLOW)
1075                         {
1076                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str());
1077                                 found = c;
1078                                 break;
1079                         }
1080
1081                         if (c->type == CC_NAMED)
1082                                 continue;
1083
1084                         bool regdone = (registered != REG_NONE);
1085                         if (c->config->getBool("registered", regdone) != regdone)
1086                                 continue;
1087
1088                         /* check if host matches.. */
1089                         if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) &&
1090                             !InspIRCd::MatchCIDR(this->GetRealHost(), c->GetHost(), NULL))
1091                         {
1092                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "No host match (for %s)", c->GetHost().c_str());
1093                                 continue;
1094                         }
1095
1096                         /*
1097                          * deny change if change will take class over the limit check it HERE, not after we found a matching class,
1098                          * because we should attempt to find another class if this one doesn't match us. -- w00t
1099                          */
1100                         if (c->limit && (c->GetReferenceCount() >= c->limit))
1101                         {
1102                                 ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit);
1103                                 continue;
1104                         }
1105
1106                         /* if it requires a port ... */
1107                         if (!c->ports.empty())
1108                         {
1109                                 /* and our port doesn't match, fail. */
1110                                 if (!c->ports.count(this->server_sa.port()))
1111                                 {
1112                                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires a different port, skipping");
1113                                         continue;
1114                                 }
1115                         }
1116
1117                         if (regdone && !c->config->getString("password").empty())
1118                         {
1119                                 if (!ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash")))
1120                                 {
1121                                         ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Bad password, skipping");
1122                                         continue;
1123                                 }
1124                         }
1125
1126                         /* we stop at the first class that meets ALL critera. */
1127                         found = c;
1128                         break;
1129                 }
1130         }
1131
1132         /*
1133          * Okay, assuming we found a class that matches.. switch us into that class, keeping refcounts up to date.
1134          */
1135         if (found)
1136         {
1137                 MyClass = found;
1138         }
1139 }
1140
1141 void User::PurgeEmptyChannels()
1142 {
1143         // firstly decrement the count on each channel
1144         for (User::ChanList::iterator i = this->chans.begin(); i != this->chans.end(); )
1145         {
1146                 Channel* c = (*i)->chan;
1147                 ++i;
1148                 c->DelUser(this);
1149         }
1150 }
1151
1152 void User::WriteNotice(const std::string& text)
1153 {
1154         LocalUser* const localuser = IS_LOCAL(this);
1155         if (!localuser)
1156                 return;
1157
1158         ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, localuser, text, MSG_NOTICE);
1159         localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
1160 }
1161
1162 const std::string& FakeUser::GetFullHost()
1163 {
1164         if (!ServerInstance->Config->HideServer.empty())
1165                 return ServerInstance->Config->HideServer;
1166         return server->GetName();
1167 }
1168
1169 const std::string& FakeUser::GetFullRealHost()
1170 {
1171         if (!ServerInstance->Config->HideServer.empty())
1172                 return ServerInstance->Config->HideServer;
1173         return server->GetName();
1174 }
1175
1176 ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask)
1177         : config(tag), type(t), fakelag(true), name("unnamed"), registration_timeout(0), host(mask),
1178         pingtime(0), softsendqmax(0), hardsendqmax(0), recvqmax(0),
1179         penaltythreshold(0), commandrate(0), maxlocal(0), maxglobal(0), maxconnwarn(true), maxchans(ServerInstance->Config->MaxChans),
1180         limit(0), resolvehostnames(true)
1181 {
1182 }
1183
1184 ConnectClass::ConnectClass(ConfigTag* tag, char t, const std::string& mask, const ConnectClass& parent)
1185 {
1186         Update(&parent);
1187         name = "unnamed";
1188         type = t;
1189         host = mask;
1190
1191         // Connect classes can inherit from each other but this is problematic for modules which can't use
1192         // ConnectClass::Update so we build a hybrid tag containing all of the values set on this class as
1193         // well as the parent class.
1194         ConfigItems* items = NULL;
1195         config = ConfigTag::create(tag->tag, tag->src_name, tag->src_line, items);
1196
1197         const ConfigItems& parentkeys = parent.config->getItems();
1198         for (ConfigItems::const_iterator piter = parentkeys.begin(); piter != parentkeys.end(); ++piter)
1199         {
1200                 // The class name and parent name are not inherited
1201                 if (stdalgo::string::equalsci(piter->first, "name") || stdalgo::string::equalsci(piter->first, "parent"))
1202                         continue;
1203
1204                 // Store the item in the config tag. If this item also
1205                 // exists in the child it will be overwritten.
1206                 (*items)[piter->first] = piter->second;
1207         }
1208
1209         const ConfigItems& childkeys = tag->getItems();
1210         for (ConfigItems::const_iterator citer = childkeys.begin(); citer != childkeys.end(); ++citer)
1211         {
1212                 // This will overwrite the parent value if present.
1213                 (*items)[citer->first] = citer->second;
1214         }
1215 }
1216
1217 void ConnectClass::Update(const ConnectClass* src)
1218 {
1219         config = src->config;
1220         type = src->type;
1221         fakelag = src->fakelag;
1222         name = src->name;
1223         registration_timeout = src->registration_timeout;
1224         host = src->host;
1225         pingtime = src->pingtime;
1226         softsendqmax = src->softsendqmax;
1227         hardsendqmax = src->hardsendqmax;
1228         recvqmax = src->recvqmax;
1229         penaltythreshold = src->penaltythreshold;
1230         commandrate = src->commandrate;
1231         maxlocal = src->maxlocal;
1232         maxglobal = src->maxglobal;
1233         maxconnwarn = src->maxconnwarn;
1234         maxchans = src->maxchans;
1235         limit = src->limit;
1236         resolvehostnames = src->resolvehostnames;
1237         ports = src->ports;
1238 }