]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/coremods/core_whowas.cpp
Merge insp20
[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                 if (!list.empty())
51                 {
52                         for (WhoWas::Nick::List::const_iterator i = list.begin(); i != list.end(); ++i)
53                         {
54                                 WhoWas::Entry* u = *i;
55
56                                 user->WriteNumeric(RPL_WHOWASUSER, "%s %s %s * :%s", parameters[0].c_str(),
57                                         u->ident.c_str(),u->dhost.c_str(),u->gecos.c_str());
58
59                                 if (user->HasPrivPermission("users/auspex"))
60                                         user->WriteNumeric(RPL_WHOWASIP, "%s :was connecting from *@%s",
61                                                 parameters[0].c_str(), u->host.c_str());
62
63                                 std::string signon = InspIRCd::TimeString(u->signon);
64                                 bool hide_server = (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex"));
65                                 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());
66                         }
67                 }
68         }
69
70         user->WriteNumeric(RPL_ENDOFWHOWAS, "%s :End of WHOWAS", parameters[0].c_str());
71         return CMD_SUCCESS;
72 }
73
74 WhoWas::Manager::Manager()
75         : GroupSize(0), MaxGroups(0), MaxKeep(0)
76 {
77 }
78
79 const WhoWas::Nick* WhoWas::Manager::FindNick(const std::string& nickname) const
80 {
81         whowas_users::const_iterator it = whowas.find(nickname);
82         if (it == whowas.end())
83                 return NULL;
84
85         const Nick* nick = it->second;
86         if (nick->entries.empty())
87                 return NULL;
88         return nick;
89 }
90
91 WhoWas::Manager::Stats WhoWas::Manager::GetStats() const
92 {
93         size_t entrycount = 0;
94         for (whowas_users::const_iterator i = whowas.begin(); i != whowas.end(); ++i)
95         {
96                 WhoWas::Nick::List& list = i->second->entries;
97                 entrycount += list.size();
98         }
99
100         Stats stats;
101         stats.entrycount = entrycount;
102         return stats;
103 }
104
105 void WhoWas::Manager::Add(User* user)
106 {
107         if (!IsEnabled())
108                 return;
109
110         // Insert nick if it doesn't exist
111         // 'first' will point to the newly inserted element or to the existing element with an equivalent key
112         std::pair<whowas_users::iterator, bool> ret = whowas.insert(std::make_pair(user->nick, static_cast<WhoWas::Nick*>(NULL)));
113
114         if (ret.second) // If inserted
115         {
116                 // This nick is new, create a list for it and add the first record to it
117                 WhoWas::Nick* nick = new WhoWas::Nick(ret.first->first);
118                 nick->entries.push_back(new Entry(user));
119                 ret.first->second = nick;
120
121                 // Add this nick to the fifo too
122                 whowas_fifo.push_back(nick);
123
124                 if (whowas.size() > this->MaxGroups)
125                 {
126                         // Too many nicks, remove the nick which was inserted the longest time ago from both the map and the fifo
127                         nick = whowas_fifo.front();
128                         whowas_fifo.pop_front();
129                         whowas.erase(nick->nick);
130                         delete nick;
131                 }
132         }
133         else
134         {
135                 // We've met this nick before, add a new record to the list
136                 WhoWas::Nick::List& list = ret.first->second->entries;
137                 list.push_back(new Entry(user));
138
139                 // If there are too many records for this nick, remove the oldest (front)
140                 if (list.size() > this->GroupSize)
141                 {
142                         delete list.front();
143                         list.pop_front();
144                 }
145         }
146 }
147
148 /* on rehash, refactor maps according to new conf values */
149 void WhoWas::Manager::Prune()
150 {
151         time_t min = ServerInstance->Time() - this->MaxKeep;
152
153         /* first cut the list to new size (maxgroups) and also prune entries that are timed out. */
154         while (!whowas_fifo.empty())
155         {
156                 WhoWas::Nick* nick = whowas_fifo.front();
157                 if ((whowas_fifo.size() > this->MaxGroups) || (nick->addtime < min))
158                 {
159                         /* hopefully redundant integrity check, but added while debugging r6216 */
160                         if (!whowas.erase(nick->nick))
161                         {
162                                 /* this should never happen, if it does maps are corrupt */
163                                 ServerInstance->Logs->Log("WHOWAS", LOG_DEFAULT, "BUG: Whowas maps got corrupted! (1)");
164                                 return;
165                         }
166
167                         whowas_fifo.pop_front();
168                         delete nick;
169                 }
170                 else
171                         break;
172         }
173
174         /* Then cut the whowas sets to new size (groupsize) */
175         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
176         {
177                 WhoWas::Nick::List& list = i->second->entries;
178                 while (list.size() > this->GroupSize)
179                 {
180                         delete list.front();
181                         list.pop_front();
182                 }
183         }
184 }
185
186 /* call maintain once an hour to remove expired nicks */
187 void WhoWas::Manager::Maintain()
188 {
189         time_t min = ServerInstance->Time() - this->MaxKeep;
190         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
191         {
192                 WhoWas::Nick::List& list = i->second->entries;
193                 while (!list.empty() && list.front()->signon < min)
194                 {
195                         delete list.front();
196                         list.pop_front();
197                 }
198         }
199 }
200
201 WhoWas::Manager::~Manager()
202 {
203         for (whowas_users::iterator i = whowas.begin(); i != whowas.end(); ++i)
204         {
205                 WhoWas::Nick* nick = i->second;
206                 delete nick;
207         }
208 }
209
210 bool WhoWas::Manager::IsEnabled() const
211 {
212         return ((GroupSize != 0) && (MaxGroups != 0));
213 }
214
215 void WhoWas::Manager::UpdateConfig(unsigned int NewGroupSize, unsigned int NewMaxGroups, unsigned int NewMaxKeep)
216 {
217         if ((NewGroupSize == GroupSize) && (NewMaxGroups == MaxGroups) && (NewMaxKeep == MaxKeep))
218                 return;
219
220         GroupSize = NewGroupSize;
221         MaxGroups = NewMaxGroups;
222         MaxKeep = NewMaxKeep;
223         Prune();
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)