]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_whowas.cpp
core_whowas Add WhoWas::Manager::PurgeNick()
[user/henk/code/inspircd.git] / src / coremods / core_whowas.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
6  *   Copyright (C) 2008 Craig Edwards <craigedwards@brainbox.cc>
7  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
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 "commands/cmd_whowas.h"
25
26 CommandWhowas::CommandWhowas( Module* parent)
27         : Command(parent, "WHOWAS", 1)
28 {
29         syntax = "<nick>{,<nick>}";
30         Penalty = 2;
31 }
32
33 CmdResult CommandWhowas::Handle (const std::vector<std::string>& parameters, User* user)
34 {
35         /* if whowas disabled in config */
36         if (!manager.IsEnabled())
37         {
38                 user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", name.c_str());
39                 return CMD_FAILURE;
40         }
41
42         const WhoWas::Nick* const nick = manager.FindNick(parameters[0]);
43         if (!nick)
44         {
45                 user->WriteNumeric(ERR_WASNOSUCHNICK, "%s :There was no such nickname", parameters[0].c_str());
46         }
47         else
48         {
49                 const WhoWas::Nick::List& list = nick->entries;
50                 for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
51                 {
52                         WhoWas::Entry* u = *i;
53
54                         user->WriteNumeric(RPL_WHOWASUSER, "%s %s %s * :%s", parameters[0].c_str(), u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
55
56                         if (user->HasPrivPermission("users/auspex"))
57                                 user->WriteNumeric(RPL_WHOWASIP, "%s :was connecting from *@%s", parameters[0].c_str(), u->host.c_str());
58
59                         std::string signon = InspIRCd::TimeString(u->signon);
60                         bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
61                         user->WriteNumeric(RPL_WHOISSERVER, "%s %s :%s", parameters[0].c_str(), (hide_server ? ServerInstance->Config->HideWhoisServer.c_str() : u->server.c_str()), signon.c_str());
62                 }
63         }
64
65         user->WriteNumeric(RPL_ENDOFWHOWAS, "%s :End of WHOWAS", parameters[0].c_str());
66         return CMD_SUCCESS;
67 }
68
69 WhoWas::Manager::Manager()
70         : GroupSize(0), MaxGroups(0), MaxKeep(0)
71 {
72 }
73
74 const WhoWas::Nick* WhoWas::Manager::FindNick(const std::string& nickname) const
75 {
76         whowas_users::const_iterator it = whowas.find(nickname);
77         if (it == whowas.end())
78                 return NULL;
79
80         const Nick* nick = it->second;
81         if (nick->entries.empty())
82                 return NULL;
83         return nick;
84 }
85
86 WhoWas::Manager::Stats WhoWas::Manager::GetStats() const
87 {
88         size_t entrycount = 0;
89         for (whowas_users::const_iterator i = whowas.begin(); i != whowas.end(); ++i)
90         {
91                 WhoWas::Nick::List& list = i->second->entries;
92                 entrycount += list.size();
93         }
94
95         Stats stats;
96         stats.entrycount = entrycount;
97         return stats;
98 }
99
100 void WhoWas::Manager::Add(User* user)
101 {
102         if (!IsEnabled())
103                 return;
104
105         // Insert nick if it doesn't exist
106         // 'first' will point to the newly inserted element or to the existing element with an equivalent key
107         std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(user->nick, static_cast<WhoWas::Nick*>(NULL)));
108
109         if (ret.second) // If inserted
110         {
111                 // This nick is new, create a list for it and add the first record to it
112                 WhoWas::Nick* nick = new WhoWas::Nick(ret.first->first);
113                 nick->entries.push_back(new Entry(user));
114                 ret.first->second = nick;
115
116                 // Add this nick to the fifo too
117                 whowas_fifo.push_back(nick);
118
119                 if (whowas.size() > this->MaxGroups)
120                 {
121                         // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
122                         PurgeNick(whowas_fifo.front());
123                 }
124         }
125         else
126         {
127                 // We've met this nick before, add a new record to the list
128                 WhoWas::Nick::List& list = ret.first->second->entries;
129                 list.push_back(new Entry(user));
130
131                 // If there are too many records for this nick, remove the oldest (front)
132                 if (list.size() > this->GroupSize)
133                 {
134                         delete list.front();
135                         list.pop_front();
136                 }
137         }
138 }
139
140 /* on rehash, refactor maps according to new conf values */
141 void WhoWas::Manager::Prune()
142 {
143         time_t min = ServerInstance->Time() - this->MaxKeep;
144
145         /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
146         while (!whowas_fifo.empty())
147         {
148                 WhoWas::Nick* nick = whowas_fifo.front();
149                 if ((whowas_fifo.size() > this->MaxGroups) || (nick->addtime < min))
150                         PurgeNick(nick);
151                 else
152                         break;
153         }
154
155         /* Then cut the whowas sets to new size (groupsize) */
156         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
157         {
158                 WhoWas::Nick::List& list = i->second->entries;
159                 while (list.size() > this->GroupSize)
160                 {
161                         delete list.front();
162                         list.pop_front();
163                 }
164         }
165 }
166
167 /* call maintain once an hour to remove expired nicks */
168 void WhoWas::Manager::Maintain()
169 {
170         time_t min = ServerInstance->Time() - this->MaxKeep;
171         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
172         {
173                 WhoWas::Nick::List& list = i->second->entries;
174                 while (!list.empty() && list.front()->signon < min)
175                 {
176                         delete list.front();
177                         list.pop_front();
178                 }
179         }
180 }
181
182 WhoWas::Manager::~Manager()
183 {
184         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
185         {
186                 WhoWas::Nick* nick = i->second;
187                 delete nick;
188         }
189 }
190
191 bool WhoWas::Manager::IsEnabled() const
192 {
193         return ((GroupSize != 0) && (MaxGroups != 0));
194 }
195
196 void WhoWas::Manager::UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep)
197 {
198         if ((NewGroupSize == GroupSize) && (NewMaxGroups == MaxGroups) && (NewMaxKeep == MaxKeep))
199                 return;
200
201         GroupSize = NewGroupSize;
202         MaxGroups = NewMaxGroups;
203         MaxKeep = NewMaxKeep;
204         Prune();
205 }
206
207 void WhoWas::Manager::PurgeNick(whowas_users::iterator it)
208 {
209         WhoWas::Nick* nick = it->second;
210         whowas_fifo.erase(nick);
211         whowas.erase(it);
212         delete nick;
213 }
214
215 void WhoWas::Manager::PurgeNick(WhoWas::Nick* nick)
216 {
217         whowas_users::iterator it = whowas.find(nick->nick);
218         if (it == whowas.end())
219         {
220                 ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in whowas database, please report");
221                 return;
222         }
223         PurgeNick(it);
224 }
225
226 WhoWas::Entry::Entry(User* user)
227         : host(user->host)
228         , dhost(user->dhost)
229         , ident(user->ident)
230         , server(user->server->GetName())
231         , gecos(user->fullname)
232         , signon(user->signon)
233 {
234 }
235
236 WhoWas::Nick::Nick(const std::string& nickname)
237         : addtime(ServerInstance->Time())
238         , nick(nickname)
239 {
240 }
241
242 WhoWas::Nick::~Nick()
243 {
244         stdalgo::delete_all(entries);
245 }
246
247 class ModuleWhoWas : public Module
248 {
249         CommandWhowas cmd;
250
251  public:
252         ModuleWhoWas() : cmd(this)
253         {
254         }
255
256         void OnGarbageCollect()
257         {
258                 // Remove all entries older than MaxKeep
259                 cmd.manager.Maintain();
260         }
261
262         void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
263         {
264                 cmd.manager.Add(user);
265         }
266
267         ModResult OnStats(char symbol, User* user, string_list &results)
268         {
269                 if (symbol == 'z')
270                         results.push_back("249 "+user->nick+" :Whowas entries: "+ConvToStr(cmd.manager.GetStats().entrycount));
271
272                 return MOD_RES_PASSTHRU;
273         }
274
275         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
276         {
277                 ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
278                 unsigned int NewGroupSize = tag->getInt("groupsize", 10, 0, 10000);
279                 unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
280                 unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
281
282                 cmd.manager.UpdateConfig(NewGroupSize, NewMaxGroups, NewMaxKeep);
283         }
284
285         Version GetVersion()
286         {
287                 return Version("WHOWAS", VF_VENDOR);
288         }
289 };
290
291 MODULE_INIT(ModuleWhoWas)