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