]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/cmd_who.cpp
Implement feature in bug #395 reported by stealth, and tidy up a bit
[user/henk/code/inspircd.git] / src / cmd_who.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 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 "configreader.h"
16 #include "users.h"
17 #include "modules.h"
18 #include "wildcard.h"
19 #include "commands/cmd_who.h"
20
21 static char *get_first_visible_channel(userrec *u)
22 {
23         UCListIter i = u->chans.begin();
24         if (i != u->chans.end())
25         {
26                 if (!i->first->IsModeSet('s'))
27                         return i->first->name;
28         }
29
30         return "*";
31 }
32
33 bool cmd_who::whomatch(userrec* user, const char* matchtext)
34 {
35         bool realhost = false;
36         bool realname = false;
37         bool positive = true;
38         bool metadata = false;
39         bool ident = false;
40         bool away = false;
41         bool port = false;
42         char* dummy = NULL;
43
44         if (user->registered != REG_ALL)
45                 return false;
46
47         if (opt_local && !IS_LOCAL(user))
48                 return false;
49         else if (opt_far && IS_LOCAL(user))
50                 return false;
51
52         if (opt_mode)
53         {
54                 for (const char* n = matchtext; *n; n++)
55                 {
56                         if (*n == '+')
57                         {
58                                 positive = true;
59                                 continue;
60                         }
61                         else if (*n == '-')
62                         {
63                                 positive = false;
64                                 continue;
65                         }
66                         if (user->IsModeSet(*n) != positive)
67                                 return false;
68                 }
69                 return true;
70         }
71         else
72         {
73
74                 if (opt_metadata)
75                         metadata = user->GetExt(matchtext, dummy);
76                 else
77                 {
78                         if (opt_realname)
79                                 realname = match(user->fullname, matchtext);
80                         else
81                         {
82                                 if (opt_showrealhost)
83                                         realhost = match(user->host, matchtext);
84                                 else
85                                 {
86                                         if (opt_ident)
87                                                 ident = match(user->ident, matchtext);
88                                         else
89                                         {
90                                                 if (opt_port)
91                                                 {
92                                                         irc::portparser portrange(matchtext, false);
93                                                         long portno = -1;
94                                                         while ((portno = portrange.GetToken()))
95                                                                 if (portno == user->GetPort())
96                                                                         port = true;
97                                                 }
98                                                 else
99                                                 {
100                                                         if (opt_away)
101                                                                 away = match(user->awaymsg, matchtext);
102                                                 }
103                                         }
104                                 }
105                         }
106                 }
107                 return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext)));
108         }
109 }
110
111
112
113 extern "C" DllExport command_t* init_command(InspIRCd* Instance)
114 {
115         return new cmd_who(Instance);
116 }
117
118 bool cmd_who::CanView(chanrec* chan, userrec* user)
119 {
120         if (!user || !chan)
121                 return false;
122
123         /* Bug #383 - moved higher up the list, because if we are in the channel
124          * we can see all its users
125          */
126         if (chan->HasUser(user))
127                 return true;
128         /* Opers see all */
129         if (IS_OPER(user))
130                 return true;
131         /* Cant see inside a +s or a +p channel unless we are a member (see above) */
132         else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
133                 return true;
134
135         return false;
136 }
137
138 void cmd_who::SendWhoLine(userrec* user, const std::string &initial, chanrec* ch, userrec* u, std::vector<std::string> &whoresults)
139 {
140         std::string lcn = get_first_visible_channel(u);
141         chanrec* chlast = ServerInstance->FindChan(lcn);
142
143         /* Not visible to this user */
144         if (u->Visibility && !u->Visibility->VisibleTo(user))
145                 return;
146
147         std::string wholine =   initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
148                                 ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) +
149                                 " " + u->nick + " ";
150
151         /* away? */
152         if (IS_AWAY(u))
153         {
154                 wholine.append("G");
155         }
156         else
157         {
158                 wholine.append("H");
159         }
160
161         /* oper? */
162         if (IS_OPER(u))
163         {
164                 wholine.append("*");
165         }
166
167         wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
168         whoresults.push_back(wholine);
169 }
170
171 CmdResult cmd_who::Handle (const char** parameters, int pcnt, userrec *user)
172 {
173         /*
174          * XXX - RFC says:
175          *   The <name> passed to WHO is matched against users' host, server, real
176          *   name and nickname
177          * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
178          */
179
180         /* WHO options */
181         opt_viewopersonly = false;
182         opt_showrealhost = false;
183         opt_unlimit = false;
184         opt_realname = false;
185         opt_mode = false;
186         opt_ident = false;
187         opt_metadata = false;
188         opt_port = false;
189         opt_away = false;
190         opt_local = false;
191         opt_far = false;
192
193         chanrec *ch = NULL;
194         std::vector<std::string> whoresults;
195         std::string initial = "352 " + std::string(user->nick) + " ";
196
197         const char* matchtext = NULL;
198         bool usingwildcards = false;
199
200         /* Change '0' into '*' so the wildcard matcher can grok it */
201         matchtext = parameters[0];
202         if (!strcmp(matchtext,"0"))
203                 matchtext = "*";
204
205         for (const char* check = matchtext; *check; check++)
206         {
207                 if (*check == '*' || *check == '?')
208                 {
209                         usingwildcards = true;
210                         break;
211                 }
212         }
213
214         if (pcnt > 1)
215         {
216                 /* parse flags */
217                 const char *iter = parameters[1];
218
219                 while (*iter)
220                 {
221                         switch (*iter)
222                         {
223                                 case 'o':
224                                         opt_viewopersonly = true;
225                                 break;
226                                 case 'h':
227                                         if (IS_OPER(user))
228                                                 opt_showrealhost = true;
229                                 break;
230                                 case 'u':
231                                         if (IS_OPER(user))
232                                                 opt_unlimit = true;
233                                 break;
234                                 case 'r':
235                                         opt_realname = true;
236                                 break;
237                                 case 'm':
238                                         opt_mode = true;
239                                 break;
240                                 case 'M':
241                                         opt_metadata = true;
242                                 break;
243                                 case 'i':
244                                         opt_ident = true;
245                                 break;
246                                 case 'p':
247                                         opt_port = true;
248                                 break;
249                                 case 'a':
250                                         opt_away = true;
251                                 break;
252                                 case 'l':
253                                         opt_local = true;
254                                 break;
255                                 case 'f':
256                                         opt_far = true;
257                                 break;
258                         }
259
260                         *iter++;
261                 }
262         }
263
264
265         /* who on a channel? */
266         ch = ServerInstance->FindChan(matchtext);
267
268         if (ch)
269         {
270                 if (CanView(ch,user))
271                 {
272                         bool inside = ch->HasUser(user);
273         
274                         /* who on a channel. */
275                         CUList *cu = ch->GetUsers();
276         
277                         for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
278                         {
279                                 /* None of this applies if we WHO ourselves */
280                                 if (user != i->first)
281                                 {
282                                         /* opers only, please */
283                                         if (opt_viewopersonly && !IS_OPER(i->first))
284                                                 continue;
285         
286                                         /* If we're not inside the channel, hide +i users */
287                                         if (i->first->IsModeSet('i') && !inside)
288                                                 continue;
289                                 }
290         
291                                 SendWhoLine(user, initial, ch, i->first, whoresults);
292                         }
293                 }
294         }
295         else
296         {
297                 /* Match against wildcard of nick, server or host */
298                 if (opt_viewopersonly)
299                 {
300                         /* Showing only opers */
301                         for (std::vector<userrec*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
302                         {
303                                 userrec* oper = *i;
304
305                                 if (whomatch(oper, matchtext))
306                                 {
307                                         if (!user->SharesChannelWith(oper))
308                                         {
309                                                 if (usingwildcards && (!oper->IsModeSet('i')) && (!IS_OPER(user)))
310                                                         continue;
311                                         }
312
313                                         SendWhoLine(user, initial, NULL, oper, whoresults);
314                                 }
315                         }
316                 }
317                 else
318                 {
319                         for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
320                         {
321                                 if (whomatch(i->second, matchtext))
322                                 {
323                                         if (!user->SharesChannelWith(i->second))
324                                         {
325                                                 if (usingwildcards && (i->second->IsModeSet('i')) && (!IS_OPER(user)))
326                                                         continue;
327                                         }
328
329                                         SendWhoLine(user, initial, NULL, i->second, whoresults);
330                                 }
331                         }
332                 }
333         }
334         /* Send the results out */
335         if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
336         {
337                 for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
338                         user->WriteServ(*n);
339                 user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*");
340                 return CMD_SUCCESS;
341         }
342         else
343         {
344                 /* BZZT! Too many results. */
345                 user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]);
346                 return CMD_FAILURE;
347         }
348 }
349