]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_check.cpp
Some more text fixes and improvements (#1618).
[user/henk/code/inspircd.git] / src / modules / m_check.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
6  *   Copyright (C) 2006-2007 Robin Burchell <robin+git@viroteck.net>
7  *   Copyright (C) 2006 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 "listmode.h"
25
26 enum
27 {
28         RPL_CHECK = 802
29 };
30
31 class CheckContext
32 {
33  private:
34         User* const user;
35         const std::string& target;
36
37         std::string FormatTime(time_t ts)
38         {
39                 std::string timestr(InspIRCd::TimeString(ts, "%Y-%m-%d %H:%M:%S UTC (", true));
40                 timestr.append(ConvToStr(ts));
41                 timestr.push_back(')');
42                 return timestr;
43         }
44
45  public:
46         CheckContext(User* u, const std::string& targetstr)
47                 : user(u)
48                 , target(targetstr)
49         {
50                 Write("START", target);
51         }
52
53         ~CheckContext()
54         {
55                 Write("END", target);
56         }
57
58         void Write(const std::string& type, const std::string& text)
59         {
60                 user->WriteRemoteNumeric(RPL_CHECK, type, text);
61         }
62
63         void Write(const std::string& type, time_t ts)
64         {
65                 user->WriteRemoteNumeric(RPL_CHECK, type, FormatTime(ts));
66         }
67
68         User* GetUser() const { return user; }
69
70         void DumpListMode(ListModeBase* mode, Channel* chan)
71         {
72                 const ListModeBase::ModeList* list = mode->GetList(chan);
73                 if (!list)
74                         return;
75
76                 for (ListModeBase::ModeList::const_iterator i = list->begin(); i != list->end(); ++i)
77                 {
78                         CheckContext::List listmode(*this, "listmode");
79                         listmode.Add(ConvToStr(mode->GetModeChar()));
80                         listmode.Add(i->mask);
81                         listmode.Add(i->setter);
82                         listmode.Add(FormatTime(i->time));
83                         listmode.Flush();
84                 }
85         }
86
87         void DumpExt(Extensible* ext)
88         {
89                 CheckContext::List extlist(*this, "metadata");
90                 for(Extensible::ExtensibleStore::const_iterator i = ext->GetExtList().begin(); i != ext->GetExtList().end(); ++i)
91                 {
92                         ExtensionItem* item = i->first;
93                         std::string value = item->serialize(FORMAT_USER, ext, i->second);
94                         if (!value.empty())
95                                 Write("meta:" + item->name, value);
96                         else if (!item->name.empty())
97                                 extlist.Add(item->name);
98                 }
99
100                 extlist.Flush();
101         }
102
103         class List : public Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>
104         {
105          public:
106                 List(CheckContext& context, const char* checktype)
107                         : Numeric::GenericBuilder<' ', false, Numeric::WriteRemoteNumericSink>(Numeric::WriteRemoteNumericSink(context.GetUser()), RPL_CHECK, false, (IS_LOCAL(context.GetUser()) ? context.GetUser()->nick.length() : ServerInstance->Config->Limits.NickMax) + strlen(checktype) + 1)
108                 {
109                         GetNumeric().push(checktype).push(std::string());
110                 }
111         };
112 };
113
114 /** Handle /CHECK
115  */
116 class CommandCheck : public Command
117 {
118         UserModeReference snomaskmode;
119
120         std::string GetSnomasks(User* user)
121         {
122                 std::string ret;
123                 if (snomaskmode)
124                         ret = snomaskmode->GetUserParameter(user);
125
126                 if (ret.empty())
127                         ret = "+";
128                 return ret;
129         }
130
131         static std::string GetAllowedOperOnlyModes(LocalUser* user, ModeType modetype)
132         {
133                 std::string ret;
134                 const ModeParser::ModeHandlerMap& modes = ServerInstance->Modes.GetModes(modetype);
135                 for (ModeParser::ModeHandlerMap::const_iterator i = modes.begin(); i != modes.end(); ++i)
136                 {
137                         const ModeHandler* const mh = i->second;
138                         if ((mh->NeedsOper()) && (user->HasModePermission(mh)))
139                                 ret.push_back(mh->GetModeChar());
140                 }
141                 return ret;
142         }
143
144  public:
145         CommandCheck(Module* parent)
146                 : Command(parent,"CHECK", 1)
147                 , snomaskmode(parent, "snomask")
148         {
149                 flags_needed = 'o'; syntax = "<nick>|<ipmask>|<hostmask>|<channel> [<servername>]";
150         }
151
152         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
153         {
154                 if (parameters.size() > 1 && !irc::equals(parameters[1], ServerInstance->Config->ServerName))
155                         return CMD_SUCCESS;
156
157                 User *targuser;
158                 Channel *targchan;
159                 std::string chliststr;
160
161                 targuser = ServerInstance->FindNick(parameters[0]);
162                 targchan = ServerInstance->FindChan(parameters[0]);
163
164                 /*
165                  * Syntax of a /check reply:
166                  *  :server.name 802 target START <target>
167                  *  :server.name 802 target <field> :<value>
168                  *  :server.name 802 target END <target>
169                  */
170
171                 // Constructor sends START, destructor sends END
172                 CheckContext context(user, parameters[0]);
173
174                 if (targuser)
175                 {
176                         LocalUser* loctarg = IS_LOCAL(targuser);
177                         /* /check on a user */
178                         context.Write("nuh", targuser->GetFullHost());
179                         context.Write("realnuh", targuser->GetFullRealHost());
180                         context.Write("realname", targuser->GetRealName());
181                         context.Write("modes", targuser->GetModeLetters());
182                         context.Write("snomasks", GetSnomasks(targuser));
183                         context.Write("server", targuser->server->GetName());
184                         context.Write("uid", targuser->uuid);
185                         context.Write("signon", targuser->signon);
186                         context.Write("nickts", targuser->age);
187                         if (loctarg)
188                                 context.Write("lastmsg", loctarg->idle_lastmsg);
189
190                         if (targuser->IsAway())
191                         {
192                                 /* user is away */
193                                 context.Write("awaytime", targuser->awaytime);
194                                 context.Write("awaymsg", targuser->awaymsg);
195                         }
196
197                         if (targuser->IsOper())
198                         {
199                                 OperInfo* oper = targuser->oper;
200                                 /* user is an oper of type ____ */
201                                 context.Write("opertype", oper->name);
202                                 if (loctarg)
203                                 {
204                                         context.Write("chanmodeperms", GetAllowedOperOnlyModes(loctarg, MODETYPE_CHANNEL));
205                                         context.Write("usermodeperms", GetAllowedOperOnlyModes(loctarg, MODETYPE_USER));
206                                         context.Write("commandperms", oper->AllowedOperCommands.ToString());
207                                         context.Write("permissions", oper->AllowedPrivs.ToString());
208                                 }
209                         }
210
211                         if (loctarg)
212                         {
213                                 context.Write("clientaddr", loctarg->client_sa.str());
214                                 context.Write("serveraddr", loctarg->server_sa.str());
215
216                                 std::string classname = loctarg->GetClass()->name;
217                                 if (!classname.empty())
218                                         context.Write("connectclass", classname);
219                         }
220                         else
221                                 context.Write("onip", targuser->GetIPString());
222
223                         CheckContext::List chanlist(context, "onchans");
224                         for (User::ChanList::iterator i = targuser->chans.begin(); i != targuser->chans.end(); i++)
225                         {
226                                 Membership* memb = *i;
227                                 Channel* c = memb->chan;
228                                 char prefix = memb->GetPrefixChar();
229                                 if (prefix)
230                                         chliststr.push_back(prefix);
231                                 chliststr.append(c->name);
232                                 chanlist.Add(chliststr);
233                                 chliststr.clear();
234                         }
235
236                         chanlist.Flush();
237
238                         context.DumpExt(targuser);
239                 }
240                 else if (targchan)
241                 {
242                         /* /check on a channel */
243                         context.Write("createdat", targchan->age);
244
245                         if (!targchan->topic.empty())
246                         {
247                                 /* there is a topic, assume topic related information exists */
248                                 context.Write("topic", targchan->topic);
249                                 context.Write("topic_setby", targchan->setby);
250                                 context.Write("topic_setat", targchan->topicset);
251                         }
252
253                         context.Write("modes", targchan->ChanModes(true));
254                         context.Write("membercount", ConvToStr(targchan->GetUserCounter()));
255
256                         /* now the ugly bit, spool current members of a channel. :| */
257
258                         const Channel::MemberMap& ulist = targchan->GetUsers();
259
260                         /* note that unlike /names, we do NOT check +i vs in the channel */
261                         for (Channel::MemberMap::const_iterator i = ulist.begin(); i != ulist.end(); ++i)
262                         {
263                                 /*
264                                  * Unlike Asuka, I define a clone as coming from the same host. --w00t
265                                  */
266                                 const UserManager::CloneCounts& clonecount = ServerInstance->Users->GetCloneCounts(i->first);
267                                 context.Write("member", InspIRCd::Format("%u %s%s (%s)", clonecount.global,
268                                         i->second->GetAllPrefixChars().c_str(), i->first->GetFullHost().c_str(),
269                                         i->first->GetRealName().c_str()));
270                         }
271
272                         const ModeParser::ListModeList& listmodes = ServerInstance->Modes->GetListModes();
273                         for (ModeParser::ListModeList::const_iterator i = listmodes.begin(); i != listmodes.end(); ++i)
274                                 context.DumpListMode(*i, targchan);
275
276                         context.DumpExt(targchan);
277                 }
278                 else
279                 {
280                         /*  /check on an IP address, or something that doesn't exist */
281                         long x = 0;
282
283                         /* hostname or other */
284                         const user_hash& users = ServerInstance->Users->GetUsers();
285                         for (user_hash::const_iterator a = users.begin(); a != users.end(); ++a)
286                         {
287                                 if (InspIRCd::Match(a->second->GetRealHost(), parameters[0], ascii_case_insensitive_map) || InspIRCd::Match(a->second->GetDisplayedHost(), parameters[0], ascii_case_insensitive_map))
288                                 {
289                                         /* host or vhost matches mask */
290                                         context.Write("match", ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->GetRealName());
291                                 }
292                                 /* IP address */
293                                 else if (InspIRCd::MatchCIDR(a->second->GetIPString(), parameters[0]))
294                                 {
295                                         /* same IP. */
296                                         context.Write("match", ConvToStr(++x) + " " + a->second->GetFullRealHost() + " " + a->second->GetIPString() + " " + a->second->GetRealName());
297                                 }
298                         }
299
300                         context.Write("matches", ConvToStr(x));
301                 }
302
303                 // END is sent by the CheckContext destructor
304                 return CMD_SUCCESS;
305         }
306
307         RouteDescriptor GetRouting(User* user, const Params& parameters) CXX11_OVERRIDE
308         {
309                 if ((parameters.size() > 1) && (parameters[1].find('.') != std::string::npos))
310                         return ROUTE_OPT_UCAST(parameters[1]);
311                 return ROUTE_LOCALONLY;
312         }
313 };
314
315 class ModuleCheck : public Module
316 {
317         CommandCheck mycommand;
318  public:
319         ModuleCheck() : mycommand(this)
320         {
321         }
322
323         Version GetVersion() CXX11_OVERRIDE
324         {
325                 return Version("Provides the CHECK command to view user, channel, IP address or hostname information", VF_VENDOR|VF_OPTCOMMON);
326         }
327 };
328
329 MODULE_INIT(ModuleCheck)