]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_whowas.cpp
79ce94cc9f8f8b3603816c2ec43d9976b698d1dc
[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         return it->second;
80 }
81
82 WhoWas::Manager::Stats WhoWas::Manager::GetStats() const
83 {
84         size_t entrycount = 0;
85         for (whowas_users::const_iterator i = whowas.begin(); i != whowas.end(); ++i)
86         {
87                 WhoWas::Nick::List& list = i->second->entries;
88                 entrycount += list.size();
89         }
90
91         Stats stats;
92         stats.entrycount = entrycount;
93         return stats;
94 }
95
96 void WhoWas::Manager::Add(User* user)
97 {
98         if (!IsEnabled())
99                 return;
100
101         // Insert nick if it doesn't exist
102         // 'first' will point to the newly inserted element or to the existing element with an equivalent key
103         std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(user->nick, static_cast<WhoWas::Nick*>(NULL)));
104
105         if (ret.second) // If inserted
106         {
107                 // This nick is new, create a list for it and add the first record to it
108                 WhoWas::Nick* nick = new WhoWas::Nick(ret.first->first);
109                 nick->entries.push_back(new Entry(user));
110                 ret.first->second = nick;
111
112                 // Add this nick to the fifo too
113                 whowas_fifo.push_back(nick);
114
115                 if (whowas.size() > this->MaxGroups)
116                 {
117                         // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
118                         PurgeNick(whowas_fifo.front());
119                 }
120         }
121         else
122         {
123                 // We've met this nick before, add a new record to the list
124                 WhoWas::Nick::List& list = ret.first->second->entries;
125                 list.push_back(new Entry(user));
126
127                 // If there are too many records for this nick, remove the oldest (front)
128                 if (list.size() > this->GroupSize)
129                 {
130                         delete list.front();
131                         list.pop_front();
132                 }
133         }
134 }
135
136 /* on rehash, refactor maps according to new conf values */
137 void WhoWas::Manager::Prune()
138 {
139         time_t min = ServerInstance->Time() - this->MaxKeep;
140
141         /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
142         while (!whowas_fifo.empty())
143         {
144                 WhoWas::Nick* nick = whowas_fifo.front();
145                 if ((whowas_fifo.size() > this->MaxGroups) || (nick->addtime < min))
146                         PurgeNick(nick);
147                 else
148                         break;
149         }
150
151         /* Then cut the whowas sets to new size (groupsize) */
152         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
153         {
154                 WhoWas::Nick::List& list = i->second->entries;
155                 while (list.size() > this->GroupSize)
156                 {
157                         delete list.front();
158                         list.pop_front();
159                 }
160
161                 if (list.empty())
162                         PurgeNick(i++);
163                 else
164                         ++i;
165         }
166 }
167
168 /* call maintain once an hour to remove expired nicks */
169 void WhoWas::Manager::Maintain()
170 {
171         time_t min = ServerInstance->Time() - this->MaxKeep;
172         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); )
173         {
174                 WhoWas::Nick::List& list = i->second->entries;
175                 while (!list.empty() && list.front()->signon < min)
176                 {
177                         delete list.front();
178                         list.pop_front();
179                 }
180
181                 if (list.empty())
182                         PurgeNick(i++);
183                 else
184                         ++i;
185         }
186 }
187
188 WhoWas::Manager::~Manager()
189 {
190         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
191         {
192                 WhoWas::Nick* nick = i->second;
193                 delete nick;
194         }
195 }
196
197 bool WhoWas::Manager::IsEnabled() const
198 {
199         return ((GroupSize != 0) && (MaxGroups != 0));
200 }
201
202 void WhoWas::Manager::UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep)
203 {
204         if ((NewGroupSize == GroupSize) && (NewMaxGroups == MaxGroups) && (NewMaxKeep == MaxKeep))
205                 return;
206
207         GroupSize = NewGroupSize;
208         MaxGroups = NewMaxGroups;
209         MaxKeep = NewMaxKeep;
210         Prune();
211 }
212
213 void WhoWas::Manager::PurgeNick(whowas_users::iterator it)
214 {
215         WhoWas::Nick* nick = it->second;
216         whowas_fifo.erase(nick);
217         whowas.erase(it);
218         delete nick;
219 }
220
221 void WhoWas::Manager::PurgeNick(WhoWas::Nick* nick)
222 {
223         whowas_users::iterator it = whowas.find(nick->nick);
224         if (it == whowas.end())
225         {
226                 ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "ERROR: Inconsistency detected in whowas database, please report");
227                 return;
228         }
229         PurgeNick(it);
230 }
231
232 WhoWas::Entry::Entry(User* user)
233         : host(user->host)
234         , dhost(user->dhost)
235         , ident(user->ident)
236         , server(user->server->GetName())
237         , gecos(user->fullname)
238         , signon(user->signon)
239 {
240 }
241
242 WhoWas::Nick::Nick(const std::string& nickname)
243         : addtime(ServerInstance->Time())
244         , nick(nickname)
245 {
246 }
247
248 WhoWas::Nick::~Nick()
249 {
250         stdalgo::delete_all(entries);
251 }
252
253 class ModuleWhoWas : public Module
254 {
255         CommandWhowas cmd;
256
257  public:
258         ModuleWhoWas() : cmd(this)
259         {
260         }
261
262         void OnGarbageCollect()
263         {
264                 // Remove all entries older than MaxKeep
265                 cmd.manager.Maintain();
266         }
267
268         void OnUserQuit(User* user, const std::string& message, const std::string& oper_message)
269         {
270                 cmd.manager.Add(user);
271         }
272
273         ModResult OnStats(char symbol, User* user, string_list &results)
274         {
275                 if (symbol == 'z')
276                         results.push_back("249 "+user->nick+" :Whowas entries: "+ConvToStr(cmd.manager.GetStats().entrycount));
277
278                 return MOD_RES_PASSTHRU;
279         }
280
281         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
282         {
283                 ConfigTag* tag = ServerInstance->Config->ConfValue("whowas");
284                 unsigned int NewGroupSize = tag->getInt("groupsize", 10, 0, 10000);
285                 unsigned int NewMaxGroups = tag->getInt("maxgroups", 10240, 0, 1000000);
286                 unsigned int NewMaxKeep = tag->getDuration("maxkeep", 3600, 3600);
287
288                 cmd.manager.UpdateConfig(NewGroupSize, NewMaxGroups, NewMaxKeep);
289         }
290
291         Version GetVersion()
292         {
293                 return Version("WHOWAS", VF_VENDOR);
294         }
295 };
296
297 MODULE_INIT(ModuleWhoWas)