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