]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/cmd_who.cpp
Move InspIRCd::IsValidMask() to helperfuncs.cpp
[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 "wildcard.h"
16 #include "commands/cmd_who.h"
17
18 static 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         std::string lcn = get_first_visible_channel(u);
138         Channel* chlast = ServerInstance->FindChan(lcn);
139
140         /* Not visible to this user */
141         if (u->Visibility && !u->Visibility->VisibleTo(user))
142                 return;
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                 while (*iter)
217                 {
218                         switch (*iter)
219                         {
220                                 case 'o':
221                                         opt_viewopersonly = true;
222                                 break;
223                                 case 'h':
224                                         if (IS_OPER(user))
225                                                 opt_showrealhost = true;
226                                 break;
227                                 case 'u':
228                                         if (IS_OPER(user))
229                                                 opt_unlimit = true;
230                                 break;
231                                 case 'r':
232                                         opt_realname = true;
233                                 break;
234                                 case 'm':
235                                         opt_mode = true;
236                                 break;
237                                 case 'M':
238                                         opt_metadata = true;
239                                 break;
240                                 case 'i':
241                                         opt_ident = true;
242                                 break;
243                                 case 'p':
244                                         opt_port = true;
245                                 break;
246                                 case 'a':
247                                         opt_away = true;
248                                 break;
249                                 case 'l':
250                                         opt_local = true;
251                                 break;
252                                 case 'f':
253                                         opt_far = true;
254                                 break;
255                         }
256
257                         *iter++;
258                 }
259         }
260
261
262         /* who on a channel? */
263         ch = ServerInstance->FindChan(matchtext);
264
265         if (ch)
266         {
267                 if (CanView(ch,user))
268                 {
269                         bool inside = ch->HasUser(user);
270         
271                         /* who on a channel. */
272                         CUList *cu = ch->GetUsers();
273         
274                         for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
275                         {
276                                 /* None of this applies if we WHO ourselves */
277                                 if (user != i->first)
278                                 {
279                                         /* opers only, please */
280                                         if (opt_viewopersonly && !IS_OPER(i->first))
281                                                 continue;
282         
283                                         /* If we're not inside the channel, hide +i users */
284                                         if (i->first->IsModeSet('i') && !inside)
285                                                 continue;
286                                 }
287         
288                                 SendWhoLine(user, initial, ch, i->first, whoresults);
289                         }
290                 }
291         }
292         else
293         {
294                 /* Match against wildcard of nick, server or host */
295                 if (opt_viewopersonly)
296                 {
297                         /* Showing only opers */
298                         for (std::list<User*>::iterator i = ServerInstance->all_opers.begin(); i != ServerInstance->all_opers.end(); i++)
299                         {
300                                 User* oper = *i;
301
302                                 if (whomatch(oper, matchtext))
303                                 {
304                                         if (!user->SharesChannelWith(oper))
305                                         {
306                                                 if (usingwildcards && (!oper->IsModeSet('i')) && (!IS_OPER(user)))
307                                                         continue;
308                                         }
309
310                                         SendWhoLine(user, initial, NULL, oper, whoresults);
311                                 }
312                         }
313                 }
314                 else
315                 {
316                         for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)
317                         {
318                                 if (whomatch(i->second, matchtext))
319                                 {
320                                         if (!user->SharesChannelWith(i->second))
321                                         {
322                                                 if (usingwildcards && (i->second->IsModeSet('i')) && (!IS_OPER(user)))
323                                                         continue;
324                                         }
325
326                                         SendWhoLine(user, initial, NULL, i->second, whoresults);
327                                 }
328                         }
329                 }
330         }
331         /* Send the results out */
332         if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
333         {
334                 for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
335                         user->WriteServ(*n);
336                 user->WriteServ("315 %s %s :End of /WHO list.",user->nick, *parameters[0] ? parameters[0] : "*");
337                 return CMD_SUCCESS;
338         }
339         else
340         {
341                 /* BZZT! Too many results. */
342                 user->WriteServ("315 %s %s :Too many results",user->nick, parameters[0]);
343                 return CMD_FAILURE;
344         }
345 }
346