]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/commands/cmd_who.cpp
6f8b91e75fff8d42af961e95216d1b0b1dcd5f0a
[user/henk/code/inspircd.git] / src / commands / cmd_who.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/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);
77                 else if (opt_ident)
78                         match = InspIRCd::Match(user->ident, matchtext);
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->GetPort())
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);
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         /* Not visible to this user */
151         if (u->Visibility && !u->Visibility->VisibleTo(user))
152                 return;
153
154         const std::string& lcn = get_first_visible_channel(u);
155         Channel* chlast = ServerInstance->FindChan(lcn);
156
157         std::string wholine =   initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
158                                 ((*ServerInstance->Config->HideWhoisServer && !user->HasPrivPermission("servers/auspex")) ? ServerInstance->Config->HideWhoisServer : u->server) +
159                                 " " + u->nick + " ";
160
161         /* away? */
162         if (IS_AWAY(u))
163         {
164                 wholine.append("G");
165         }
166         else
167         {
168                 wholine.append("H");
169         }
170
171         /* oper? */
172         if (IS_OPER(u))
173         {
174                 wholine.append("*");
175         }
176
177         wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
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                                         opt_mode = true;
254                                         break;
255                                 case 'M':
256                                         opt_metadata = true;
257                                         break;
258                                 case 'i':
259                                         opt_ident = true;
260                                         break;
261                                 case 'p':
262                                         opt_port = true;
263                                         break;
264                                 case 'a':
265                                         opt_away = true;
266                                         break;
267                                 case 'l':
268                                         opt_local = true;
269                                         break;
270                                 case 'f':
271                                         opt_far = true;
272                                         break;
273                                 case 't':
274                                         opt_time = true;
275                                         break;
276                         }
277                 }
278         }
279
280
281         /* who on a channel? */
282         ch = ServerInstance->FindChan(matchtext);
283
284         if (ch)
285         {
286                 if (CanView(ch,user))
287                 {
288                         bool inside = ch->HasUser(user);
289         
290                         /* who on a channel. */
291                         CUList *cu = ch->GetUsers();
292         
293                         for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
294                         {
295                                 /* None of this applies if we WHO ourselves */
296                                 if (user != i->first)
297                                 {
298                                         /* opers only, please */
299                                         if (opt_viewopersonly && !IS_OPER(i->first))
300                                                 continue;
301         
302                                         /* If we're not inside the channel, hide +i users */
303                                         if (i->first->IsModeSet('i') && !inside && !user->HasPrivPermission("users/auspex"))
304                                                 continue;
305                                 }
306         
307                                 SendWhoLine(user, initial, ch, i->first, whoresults);
308                         }
309                 }
310         }
311         else
312         {
313                 /* Match against wildcard of nick, server or host */
314                 if (opt_viewopersonly)
315                 {
316                         /* Showing only opers */
317                         for (std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++)
318                         {
319                                 User* oper = *i;
320
321                                 if (whomatch(oper, matchtext))
322                                 {
323                                         if (!user->SharesChannelWith(oper))
324                                         {
325                                                 if (usingwildcards && (!oper->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
326                                                         continue;
327                                         }
328
329                                         SendWhoLine(user, initial, NULL, oper, whoresults);
330                                 }
331                         }
332                 }
333                 else
334                 {
335                         for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++)
336                         {
337                                 if (whomatch(i->second, matchtext))
338                                 {
339                                         if (!user->SharesChannelWith(i->second))
340                                         {
341                                                 if (usingwildcards && (i->second->IsModeSet('i')) && (!user->HasPrivPermission("users/auspex")))
342                                                         continue;
343                                         }
344
345                                         SendWhoLine(user, initial, NULL, i->second, whoresults);
346                                 }
347                         }
348                 }
349         }
350         /* Send the results out */
351         if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
352         {
353                 for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
354                         user->WriteServ(*n);
355                 user->WriteNumeric(315, "%s %s :End of /WHO list.",user->nick.c_str(), *parameters[0].c_str() ? parameters[0].c_str() : "*");
356                 return CMD_SUCCESS;
357         }
358         else
359         {
360                 /* BZZT! Too many results. */
361                 user->WriteNumeric(315, "%s %s :Too many results",user->nick.c_str(), parameters[0].c_str());
362                 return CMD_FAILURE;
363         }
364 }
365