]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/commands/cmd_who.cpp
Add OnSendWhoLine hook, and use it in the oper hiding modules
[user/henk/code/inspircd.git] / src / commands / cmd_who.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *          the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "commands/cmd_who.h"
16
17 static const std::string star = "*";
18
19 static const std::string& get_first_visible_channel(User *u)
20 {
21         UCListIter i = u->chans.begin();
22         if (i != u->chans.end())
23         {
24                 if (!i->first->IsModeSet('s'))
25                         return i->first->name;
26         }
27
28         return star;
29 }
30
31 bool CommandWho::whomatch(User* user, const char* matchtext)
32 {
33         bool match = false;
34         bool positive = false;
35         char* dummy = NULL;
36
37         if (user->registered != REG_ALL)
38                 return false;
39
40         if (opt_local && !IS_LOCAL(user))
41                 return false;
42         else if (opt_far && IS_LOCAL(user))
43                 return false;
44
45         if (opt_mode)
46         {
47                 for (const char* n = matchtext; *n; n++)
48                 {
49                         if (*n == '+')
50                         {
51                                 positive = true;
52                                 continue;
53                         }
54                         else if (*n == '-')
55                         {
56                                 positive = false;
57                                 continue;
58                         }
59                         if (user->IsModeSet(*n) != positive)
60                                 return false;
61                 }
62                 return true;
63         }
64         else
65         {
66                 /*
67                  * This was previously one awesome pile of ugly nested if, when really, it didn't need
68                  * to be, since only one condition was ever checked, a chained if works just fine.
69                  * -- w00t
70                  */
71                 if (opt_metadata)
72                         match = user->GetExt(matchtext, dummy);
73                 else if (opt_realname)
74                         match = InspIRCd::Match(user->fullname, matchtext);
75                 else if (opt_showrealhost)
76                         match = InspIRCd::Match(user->host, matchtext, ascii_case_insensitive_map);
77                 else if (opt_ident)
78                         match = InspIRCd::Match(user->ident, matchtext, ascii_case_insensitive_map);
79                 else if (opt_port)
80                 {
81                         irc::portparser portrange(matchtext, false);
82                         long portno = -1;
83                         while ((portno = portrange.GetToken()))
84                                 if (portno == user->GetServerPort())
85                                 {
86                                         match = true;
87                                         break;
88                                 }
89                 }
90                 else if (opt_away)
91                         match = InspIRCd::Match(user->awaymsg, matchtext);
92                 else if (opt_time)
93                 {
94                         long seconds = ServerInstance->Duration(matchtext);
95
96                         // Okay, so time matching, we want all users connected `seconds' ago
97                         if (user->age >= ServerInstance->Time() - seconds)
98                                 match = true;
99                 }
100
101                 /*
102                  * Once the conditionals have been checked, only check dhost/nick/server
103                  * if they didn't match this user -- and only match if we don't find a match.
104                  *
105                  * This should make things minutely faster, and again, less ugly.
106                  * -- w00t
107                  */
108                 if (!match)
109                         match = InspIRCd::Match(user->dhost, matchtext, ascii_case_insensitive_map);
110
111                 if (!match)
112                         match = InspIRCd::Match(user->nick, matchtext);
113
114                 if (!match)
115                         match = InspIRCd::Match(user->server, matchtext);
116
117                 return match;
118         }
119 }
120
121
122
123 extern "C" DllExport Command* init_command(InspIRCd* Instance)
124 {
125         return new CommandWho(Instance);
126 }
127
128 bool CommandWho::CanView(Channel* chan, User* user)
129 {
130         if (!user || !chan)
131                 return false;
132
133         /* Bug #383 - moved higher up the list, because if we are in the channel
134          * we can see all its users
135          */
136         if (chan->HasUser(user))
137                 return true;
138         /* Opers see all */
139         if (user->HasPrivPermission("users/auspex"))
140                 return true;
141         /* Cant see inside a +s or a +p channel unless we are a member (see above) */
142         else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
143                 return true;
144
145         return false;
146 }
147
148 void CommandWho::SendWhoLine(User* user, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults)
149 {
150         const std::string& lcn = get_first_visible_channel(u);
151         Channel* chlast = ServerInstance->FindChan(lcn);
152
153         std::string wholine =   initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
154                                 ((*ServerInstance->Config->HideWhoisServer && !user->HasPrivPermission("servers/auspex")) ? ServerInstance->Config->HideWhoisServer : u->server) +
155                                 " " + u->nick + " ";
156
157         /* away? */
158         if (IS_AWAY(u))
159         {
160                 wholine.append("G");
161         }
162         else
163         {
164                 wholine.append("H");
165         }
166
167         /* oper? */
168         if (IS_OPER(u))
169         {
170                 wholine.append("*");
171         }
172
173         wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
174
175         FOREACH_MOD(I_OnSendWhoLine, OnSendWhoLine(user, u, ch, wholine));
176
177         if (!wholine.empty())
178                 whoresults.push_back(wholine);
179 }
180
181 CmdResult CommandWho::Handle (const std::vector<std::string>& parameters, User *user)
182 {
183         /*
184          * XXX - RFC says:
185          *   The <name> passed to WHO is matched against users' host, server, real
186          *   name and nickname
187          * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
188          */
189
190         /* WHO options */
191         opt_viewopersonly = false;
192         opt_showrealhost = false;
193         opt_unlimit = false;
194         opt_realname = false;
195         opt_mode = false;
196         opt_ident = false;
197         opt_metadata = false;
198         opt_port = false;
199         opt_away = false;
200         opt_local = false;
201         opt_far = false;
202         opt_time = false;
203
204         Channel *ch = NULL;
205         std::vector<std::string> whoresults;
206         std::string initial = "352 " + std::string(user->nick) + " ";
207
208         char matchtext[MAXBUF];
209         bool usingwildcards = false;
210
211         /* Change '0' into '*' so the wildcard matcher can grok it */
212         if (parameters[0] == "0")
213                 strlcpy(matchtext, "*", MAXBUF);
214         else
215                 strlcpy(matchtext, parameters[0].c_str(), MAXBUF);
216
217         for (const char* check = matchtext; *check; check++)
218         {
219                 if (*check == '*' || *check == '?')
220                 {
221                         usingwildcards = true;
222                         break;
223                 }
224         }
225
226         if (ServerInstance->FindServerName(matchtext))
227                 usingwildcards = true;
228
229         if (parameters.size() > 1)
230         {
231                 /* Fix for bug #444, WHO flags count as a wildcard */
232                 usingwildcards = true;
233
234                 for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
235                 {
236                         switch (*iter)
237                         {
238                                 case 'o':
239                                         opt_viewopersonly = true;
240                                         break;
241                                 case 'h':
242                                         if (user->HasPrivPermission("users/auspex"))
243                                                 opt_showrealhost = true;
244                                         break;
245                                 case 'u':
246                                         if (user->HasPrivPermission("users/auspex"))
247                                                 opt_unlimit = true;
248                                         break;
249                                 case 'r':
250                                         opt_realname = true;
251                                         break;
252                                 case 'm':
253                                         if (user->HasPrivPermission("users/auspex"))
254                                                 opt_mode = true;
255                                         break;
256                                 case 'M':
257                                         if (user->HasPrivPermission("users/auspex"))
258                                                 opt_metadata = true;
259                                         break;
260                                 case 'i':
261                                         opt_ident = true;
262                                         break;
263                                 case 'p':
264                                         if (user->HasPrivPermission("users/auspex"))
265                                                 opt_port = true;
266                                         break;
267                                 case 'a':
268                                         opt_away = true;
269                                         break;
270                                 case 'l':
271                                         if (user->HasPrivPermission("users/auspex") || !*ServerInstance->Config->HideWhoisServer)
272                                                 opt_local = true;
273                                         break;
274                                 case 'f':
275                                         if (user->HasPrivPermission("users/auspex") || !*ServerInstance->Config->HideWhoisServer)
276                                                 opt_far = true;
277                                         break;
278                                 case 't':
279                                         opt_time = true;
280                                         break;
281                         }
282                 }
283         }
284
285
286         /* who on a channel? */
287         ch = ServerInstance->FindChan(matchtext);
288
289         if (ch)
290         {
291                 if (CanView(ch,user))
292                 {
293                         bool inside = ch->HasUser(user);
294
295                         /* who on a channel. */
296                         CUList *cu = ch->GetUsers();
297
298                         for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
299                         {
300                                 /* None of this applies if we WHO ourselves */
301                                 if (user != i->first)
302                                 {
303                                         /* opers only, please */
304                                         if (opt_viewopersonly && !IS_OPER(i->first))
305                                                 continue;
306
307                                         /* If we're not inside the channel, hide +i users */
308                                         if (i->first->IsModeSet('i') && !inside && !user->HasPrivPermission("users/auspex"))
309                                                 continue;
310                                 }
311
312                                 SendWhoLine(user, initial, ch, i->first, whoresults);
313                         }
314                 }
315         }
316         else
317         {
318                 /* Match against wildcard of nick, server or host */
319                 if (opt_viewopersonly)
320                 {
321                         /* Showing only opers */
322                         for (std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++)
323                         {
324                                 User* oper = *i;
325
326                                 if (whomatch(oper, matchtext))
327                                 {
328                                         if (!user->SharesChannelWith(oper))
329                                         {
330                                                 if (usingwildcards && (!oper->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
331                                                         continue;
332                                         }
333
334                                         SendWhoLine(user, initial, NULL, oper, whoresults);
335                                 }
336                         }
337                 }
338                 else
339                 {
340                         for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++)
341                         {
342                                 if (whomatch(i->second, matchtext))
343                                 {
344                                         if (!user->SharesChannelWith(i->second))
345                                         {
346                                                 if (usingwildcards && (i->second->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
347                                                         continue;
348                                         }
349
350                                         SendWhoLine(user, initial, NULL, i->second, whoresults);
351                                 }
352                         }
353                 }
354         }
355         /* Send the results out */
356         if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
357         {
358                 for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
359                         user->WriteServ(*n);
360                 user->WriteNumeric(315, "%s %s :End of /WHO list.",user->nick.c_str(), *parameters[0].c_str() ? parameters[0].c_str() : "*");
361                 return CMD_SUCCESS;
362         }
363         else
364         {
365                 /* BZZT! Too many results. */
366                 user->WriteNumeric(315, "%s %s :Too many results",user->nick.c_str(), parameters[0].c_str());
367                 return CMD_FAILURE;
368         }
369 }