]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/usermanager.cpp
Move code between usermanager.(cpp|h), clarify comments
[user/henk/code/inspircd.git] / src / usermanager.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2008 Dennis Friis <peavey@inspircd.org>
6  *   Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
7  *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
8  *
9  * This file is part of InspIRCd.  InspIRCd is free software: you can
10  * redistribute it and/or modify it under the terms of the GNU General Public
11  * License as published by the Free Software Foundation, version 2.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22
23 #include "inspircd.h"
24 #include "xline.h"
25 #include "bancache.h"
26
27 UserManager::UserManager()
28         : clientlist(new user_hash)
29         , uuidlist(new user_hash)
30         , unregistered_count(0), local_count(0)
31 {
32 }
33
34 UserManager::~UserManager()
35 {
36         for (user_hash::iterator i = clientlist->begin(); i != clientlist->end(); ++i)
37         {
38                 delete i->second;
39         }
40
41         delete clientlist;
42         delete uuidlist;
43 }
44
45 /* add a client connection to the sockets list */
46 void UserManager::AddUser(int socket, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
47 {
48         /* NOTE: Calling this one parameter constructor for User automatically
49          * allocates a new UUID and places it in the hash_map.
50          */
51         LocalUser* New = NULL;
52         try
53         {
54                 New = new LocalUser(socket, client, server);
55         }
56         catch (...)
57         {
58                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "*** WTF *** Duplicated UUID! -- Crack smoking monkeys have been unleashed.");
59                 ServerInstance->SNO->WriteToSnoMask('a', "WARNING *** Duplicate UUID allocated!");
60                 return;
61         }
62         UserIOHandler* eh = &New->eh;
63
64         /* Give each of the modules an attempt to hook the user for I/O */
65         FOREACH_MOD(I_OnHookIO, OnHookIO(eh, via));
66
67         if (eh->GetIOHook())
68         {
69                 try
70                 {
71                         eh->GetIOHook()->OnStreamSocketAccept(eh, client, server);
72                 }
73                 catch (CoreException& modexcept)
74                 {
75                         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
76                 }
77         }
78
79         ServerInstance->Logs->Log("USERS", LOG_DEBUG, "New user fd: %d", socket);
80
81         this->unregistered_count++;
82
83         /* The users default nick is their UUID */
84         New->nick = New->uuid;
85         (*(this->clientlist))[New->nick] = New;
86
87         New->registered = REG_NONE;
88         New->signon = ServerInstance->Time() + ServerInstance->Config->dns_timeout;
89         New->lastping = 1;
90
91         ServerInstance->Users->AddLocalClone(New);
92         ServerInstance->Users->AddGlobalClone(New);
93
94         New->localuseriter = this->local_users.insert(local_users.end(), New);
95         local_count++;
96
97         if ((this->local_users.size() > ServerInstance->Config->SoftLimit) || (this->local_users.size() >= (unsigned int)ServerInstance->SE->GetMaxFds()))
98         {
99                 ServerInstance->SNO->WriteToSnoMask('a', "Warning: softlimit value has been reached: %d clients", ServerInstance->Config->SoftLimit);
100                 this->QuitUser(New,"No more connections allowed");
101                 return;
102         }
103
104         /*
105          * First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.
106          * See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
107          */
108         New->SetClass();
109
110         /*
111          * Check connect class settings and initialise settings into User.
112          * This will be done again after DNS resolution. -- w00t
113          */
114         New->CheckClass();
115         if (New->quitting)
116                 return;
117
118         /*
119          * even with bancache, we still have to keep User::exempt current.
120          * besides that, if we get a positive bancache hit, we still won't fuck
121          * them over if they are exempt. -- w00t
122          */
123         New->exempt = (ServerInstance->XLines->MatchesLine("E",New) != NULL);
124
125         if (BanCacheHit *b = ServerInstance->BanCache->GetHit(New->GetIPString()))
126         {
127                 if (!b->Type.empty() && !New->exempt)
128                 {
129                         /* user banned */
130                         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Positive hit for " + New->GetIPString());
131                         if (!ServerInstance->Config->MoronBanner.empty())
132                                 New->WriteNotice("*** " +  ServerInstance->Config->MoronBanner);
133                         this->QuitUser(New, b->Reason);
134                         return;
135                 }
136                 else
137                 {
138                         ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Negative hit for " + New->GetIPString());
139                 }
140         }
141         else
142         {
143                 if (!New->exempt)
144                 {
145                         XLine* r = ServerInstance->XLines->MatchesLine("Z",New);
146
147                         if (r)
148                         {
149                                 r->Apply(New);
150                                 return;
151                         }
152                 }
153         }
154
155         if (!ServerInstance->SE->AddFd(eh, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE))
156         {
157                 ServerInstance->Logs->Log("USERS", LOG_DEBUG, "Internal error on new connection");
158                 this->QuitUser(New, "Internal error handling connection");
159         }
160
161         if (ServerInstance->Config->RawLog)
162                 New->WriteNotice("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
163
164         FOREACH_MOD(I_OnSetUserIP,OnSetUserIP(New));
165         if (New->quitting)
166                 return;
167
168         FOREACH_MOD(I_OnUserInit,OnUserInit(New));
169 }
170
171 void UserManager::QuitUser(User *user, const std::string &quitreason, const char* operreason)
172 {
173         if (user->quitting)
174         {
175                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit quitting user: " + user->nick);
176                 return;
177         }
178
179         if (IS_SERVER(user))
180         {
181                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Tried to quit server user: " + user->nick);
182                 return;
183         }
184
185         user->quitting = true;
186
187         ServerInstance->Logs->Log("USERS", LOG_DEBUG, "QuitUser: %s=%s '%s'", user->uuid.c_str(), user->nick.c_str(), quitreason.c_str());
188         user->Write("ERROR :Closing link: (%s@%s) [%s]", user->ident.c_str(), user->host.c_str(), *operreason ? operreason : quitreason.c_str());
189
190         std::string reason;
191         std::string oper_reason;
192         reason.assign(quitreason, 0, ServerInstance->Config->Limits.MaxQuit);
193         if (operreason && *operreason)
194                 oper_reason.assign(operreason, 0, ServerInstance->Config->Limits.MaxQuit);
195         else
196                 oper_reason = quitreason;
197
198         ServerInstance->GlobalCulls.AddItem(user);
199
200         if (user->registered == REG_ALL)
201         {
202                 FOREACH_MOD(I_OnUserQuit,OnUserQuit(user, reason, oper_reason));
203                 user->WriteCommonQuit(reason, oper_reason);
204         }
205
206         if (user->registered != REG_ALL)
207                 if (ServerInstance->Users->unregistered_count)
208                         ServerInstance->Users->unregistered_count--;
209
210         if (IS_LOCAL(user))
211         {
212                 LocalUser* lu = IS_LOCAL(user);
213                 FOREACH_MOD(I_OnUserDisconnect,OnUserDisconnect(lu));
214                 lu->eh.Close();
215         }
216
217         /*
218          * this must come before the ServerInstance->SNO->WriteToSnoMaskso that it doesnt try to fill their buffer with anything
219          * if they were an oper with +s +qQ.
220          */
221         if (user->registered == REG_ALL)
222         {
223                 if (IS_LOCAL(user))
224                 {
225                         if (!user->quietquit)
226                         {
227                                 ServerInstance->SNO->WriteToSnoMask('q',"Client exiting: %s (%s) [%s]",
228                                         user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_reason.c_str());
229                         }
230                 }
231                 else
232                 {
233                         if ((!ServerInstance->SilentULine(user->server)) && (!user->quietquit))
234                         {
235                                 ServerInstance->SNO->WriteToSnoMask('Q',"Client exiting on server %s: %s (%s) [%s]",
236                                         user->server.c_str(), user->GetFullRealHost().c_str(), user->GetIPString().c_str(), oper_reason.c_str());
237                         }
238                 }
239         }
240
241         user_hash::iterator iter = this->clientlist->find(user->nick);
242
243         if (iter != this->clientlist->end())
244                 this->clientlist->erase(iter);
245         else
246                 ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Nick not found in clientlist, cannot remove: " + user->nick);
247
248         ServerInstance->Users->uuidlist->erase(user->uuid);
249 }
250
251 void UserManager::AddLocalClone(User *user)
252 {
253         local_clones[user->GetCIDRMask()]++;
254 }
255
256 void UserManager::AddGlobalClone(User *user)
257 {
258         global_clones[user->GetCIDRMask()]++;
259 }
260
261 void UserManager::RemoveCloneCounts(User *user)
262 {
263         if (IS_LOCAL(user))
264         {
265                 clonemap::iterator x = local_clones.find(user->GetCIDRMask());
266                 if (x != local_clones.end())
267                 {
268                         x->second--;
269                         if (!x->second)
270                         {
271                                 local_clones.erase(x);
272                         }
273                 }
274         }
275
276         clonemap::iterator y = global_clones.find(user->GetCIDRMask());
277         if (y != global_clones.end())
278         {
279                 y->second--;
280                 if (!y->second)
281                 {
282                         global_clones.erase(y);
283                 }
284         }
285 }
286
287 unsigned long UserManager::GlobalCloneCount(User *user)
288 {
289         clonemap::iterator x = global_clones.find(user->GetCIDRMask());
290         if (x != global_clones.end())
291                 return x->second;
292         else
293                 return 0;
294 }
295
296 unsigned long UserManager::LocalCloneCount(User *user)
297 {
298         clonemap::iterator x = local_clones.find(user->GetCIDRMask());
299         if (x != local_clones.end())
300                 return x->second;
301         else
302                 return 0;
303 }
304
305 void UserManager::ServerNoticeAll(const char* text, ...)
306 {
307         std::string message;
308         VAFORMAT(message, text, text);
309         message = "NOTICE $" + ServerInstance->Config->ServerName + " :" + message;
310
311         for (LocalUserList::const_iterator i = local_users.begin(); i != local_users.end(); i++)
312         {
313                 User* t = *i;
314                 t->WriteServ(message);
315         }
316 }
317
318 /* return how many users have a given mode e.g. 'a' */
319 int UserManager::ModeCount(const char mode)
320 {
321         int c = 0;
322         for(user_hash::iterator i = clientlist->begin(); i != clientlist->end(); ++i)
323         {
324                 User* u = i->second;
325                 if (u->modes[mode-65])
326                         c++;
327         }
328         return c;
329 }
330
331 void UserManager::GarbageCollect()
332 {
333         // Reset the already_sent IDs so we don't wrap it around and drop a message
334         LocalUser::already_sent_id = 0;
335         for (LocalUserList::const_iterator i = this->local_users.begin(); i != this->local_users.end(); i++)
336         {
337                 (**i).already_sent = 0;
338                 (**i).RemoveExpiredInvites();
339         }
340 }
341
342 /* this returns true when all modules are satisfied that the user should be allowed onto the irc server
343  * (until this returns true, a user will block in the waiting state, waiting to connect up to the
344  * registration timeout maximum seconds)
345  */
346 bool UserManager::AllModulesReportReady(LocalUser* user)
347 {
348         ModResult res;
349         FIRST_MOD_RESULT(OnCheckReady, res, (user));
350         return (res == MOD_RES_PASSTHRU);
351 }