]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/commands/cmd_who.cpp
All working now, with any luck
[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 char *get_first_visible_channel(User *u)
19 {
20         UCListIter i = u->chans.begin();
21         if (i != u->chans.end())
22         {
23                 if (!i->first->IsModeSet('s'))
24                         return i->first->name;
25         }
26
27         return "*";
28 }
29
30 bool CommandWho::whomatch(User* user, const char* matchtext)
31 {
32         bool realhost = false;
33         bool realname = false;
34         bool positive = true;
35         bool metadata = false;
36         bool ident = false;
37         bool away = false;
38         bool port = false;
39         char* dummy = NULL;
40
41         if (user->registered != REG_ALL)
42                 return false;
43
44         if (opt_local && !IS_LOCAL(user))
45                 return false;
46         else if (opt_far && IS_LOCAL(user))
47                 return false;
48
49         if (opt_mode)
50         {
51                 for (const char* n = matchtext; *n; n++)
52                 {
53                         if (*n == '+')
54                         {
55                                 positive = true;
56                                 continue;
57                         }
58                         else if (*n == '-')
59                         {
60                                 positive = false;
61                                 continue;
62                         }
63                         if (user->IsModeSet(*n) != positive)
64                                 return false;
65                 }
66                 return true;
67         }
68         else
69         {
70
71                 if (opt_metadata)
72                         metadata = user->GetExt(matchtext, dummy);
73                 else
74                 {
75                         if (opt_realname)
76                                 realname = match(user->fullname, matchtext);
77                         else
78                         {
79                                 if (opt_showrealhost)
80                                         realhost = match(user->host, matchtext);
81                                 else
82                                 {
83                                         if (opt_ident)
84                                                 ident = match(user->ident, matchtext);
85                                         else
86                                         {
87                                                 if (opt_port)
88                                                 {
89                                                         irc::portparser portrange(matchtext, false);
90                                                         long portno = -1;
91                                                         while ((portno = portrange.GetToken()))
92                                                                 if (portno == user->GetPort())
93                                                                         port = true;
94                                                 }
95                                                 else
96                                                 {
97                                                         if (opt_away)
98                                                                 away = match(user->awaymsg, matchtext);
99                                                 }
100                                         }
101                                 }
102                         }
103                 }
104                 return ((port) || (away) || (ident) || (metadata) || (realname) || (realhost) || (match(user->dhost, matchtext)) || (match(user->nick, matchtext)) || (match(user->server, matchtext)));
105         }
106 }
107
108
109
110 extern "C" DllExport Command* init_command(InspIRCd* Instance)
111 {
112         return new CommandWho(Instance);
113 }
114
115 bool CommandWho::CanView(Channel* chan, User* user)
116 {
117         if (!user || !chan)
118                 return false;
119
120         /* Bug #383 - moved higher up the list, because if we are in the channel
121          * we can see all its users
122          */
123         if (chan->HasUser(user))
124                 return true;
125         /* Opers see all */
126         if (IS_OPER(user))
127                 return true;
128         /* Cant see inside a +s or a +p channel unless we are a member (see above) */
129         else if (!chan->IsModeSet('s') && !chan->IsModeSet('p'))
130                 return true;
131
132         return false;
133 }
134
135 void CommandWho::SendWhoLine(User* user, const std::string &initial, Channel* ch, User* u, std::vector<std::string> &whoresults)
136 {
137         /* Not visible to this user */
138         if (u->Visibility && !u->Visibility->VisibleTo(user))
139                 return;
140
141         std::string lcn = get_first_visible_channel(u);
142         Channel* chlast = ServerInstance->FindChan(lcn);
143
144         std::string wholine =   initial + (ch ? ch->name : lcn) + " " + u->ident + " " + (opt_showrealhost ? u->host : u->dhost) + " " +
145                                 ((*ServerInstance->Config->HideWhoisServer && !IS_OPER(user)) ? ServerInstance->Config->HideWhoisServer : u->server) +
146                                 " " + u->nick + " ";
147
148         /* away? */
149         if (IS_AWAY(u))
150         {
151                 wholine.append("G");
152         }
153         else
154         {
155                 wholine.append("H");
156         }
157
158         /* oper? */
159         if (IS_OPER(u))
160         {
161                 wholine.append("*");
162         }
163
164         wholine = wholine + (ch ? ch->GetPrefixChar(u) : (chlast ? chlast->GetPrefixChar(u) : "")) + " :0 " + u->fullname;
165         whoresults.push_back(wholine);
166 }
167
168 CmdResult CommandWho::Handle (const char** parameters, int pcnt, User *user)
169 {
170         /*
171          * XXX - RFC says:
172          *   The <name> passed to WHO is matched against users' host, server, real
173          *   name and nickname
174          * Currently, we support WHO #chan, WHO nick, WHO 0, WHO *, and the addition of a 'o' flag, as per RFC.
175          */
176
177         /* WHO options */
178         opt_viewopersonly = false;
179         opt_showrealhost = false;
180         opt_unlimit = false;
181         opt_realname = false;
182         opt_mode = false;
183         opt_ident = false;
184         opt_metadata = false;
185         opt_port = false;
186         opt_away = false;
187         opt_local = false;
188         opt_far = false;
189
190         Channel *ch = NULL;
191         std::vector<std::string> whoresults;
192         std::string initial = "352 " + std::string(user->nick) + " ";
193
194         const char* matchtext = NULL;
195         bool usingwildcards = false;
196
197         /* Change '0' into '*' so the wildcard matcher can grok it */
198         matchtext = parameters[0];
199         if (!strcmp(matchtext,"0"))
200                 matchtext = "*";
201
202         for (const char* check = matchtext; *check; check++)
203         {
204                 if (*check == '*' || *check == '?')
205                 {
206                         usingwildcards = true;
207                         break;
208                 }
209         }
210
211         if (pcnt > 1)
212         {
213                 /* parse flags */
214                 const char *iter = parameters[1];
215
216                 /* Fix for bug #444, WHO flags count as a wildcard */
217                 usingwildcards = true;
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 && !IS_OPER(user))
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::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++)
302                         {
303                                 User* 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->Users->clientlist->begin(); i != ServerInstance->Users->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