]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/commands/cmd_who.cpp
code tidyups
[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 std::vector<std::string>& parameters, 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         char matchtext[MAXBUF];
195         bool usingwildcards = false;
196
197         /* Change '0' into '*' so the wildcard matcher can grok it */
198         if (parameters[0] == "0")
199                 strlcpy(matchtext, "*", MAXBUF);
200         else
201                 strlcpy(matchtext, parameters[0].c_str(), MAXBUF);
202
203         for (const char* check = matchtext; *check; check++)
204         {
205                 if (*check == '*' || *check == '?')
206                 {
207                         usingwildcards = true;
208                         break;
209                 }
210         }
211
212         if (ServerInstance->FindServerName(matchtext))
213                 usingwildcards = true;
214
215         if (parameters.size() > 1)
216         {
217                 /* Fix for bug #444, WHO flags count as a wildcard */
218                 usingwildcards = true;
219
220                 for (std::string::const_iterator iter = parameters[1].begin(); iter != parameters[1].end(); ++iter)
221                 {
222                         switch (*iter)
223                         {
224                                 case 'o':
225                                         opt_viewopersonly = true;
226                                 break;
227                                 case 'h':
228                                         if (IS_OPER(user))
229                                                 opt_showrealhost = true;
230                                 break;
231                                 case 'u':
232                                         if (IS_OPER(user))
233                                                 opt_unlimit = true;
234                                 break;
235                                 case 'r':
236                                         opt_realname = true;
237                                 break;
238                                 case 'm':
239                                         opt_mode = true;
240                                 break;
241                                 case 'M':
242                                         opt_metadata = true;
243                                 break;
244                                 case 'i':
245                                         opt_ident = true;
246                                 break;
247                                 case 'p':
248                                         opt_port = true;
249                                 break;
250                                 case 'a':
251                                         opt_away = true;
252                                 break;
253                                 case 'l':
254                                         opt_local = true;
255                                 break;
256                                 case 'f':
257                                         opt_far = true;
258                                 break;
259                         }
260                 }
261         }
262
263
264         /* who on a channel? */
265         ch = ServerInstance->FindChan(matchtext);
266
267         if (ch)
268         {
269                 if (CanView(ch,user))
270                 {
271                         bool inside = ch->HasUser(user);
272         
273                         /* who on a channel. */
274                         CUList *cu = ch->GetUsers();
275         
276                         for (CUList::iterator i = cu->begin(); i != cu->end(); i++)
277                         {
278                                 /* None of this applies if we WHO ourselves */
279                                 if (user != i->first)
280                                 {
281                                         /* opers only, please */
282                                         if (opt_viewopersonly && !IS_OPER(i->first))
283                                                 continue;
284         
285                                         /* If we're not inside the channel, hide +i users */
286                                         if (i->first->IsModeSet('i') && !inside && !IS_OPER(user))
287                                                 continue;
288                                 }
289         
290                                 SendWhoLine(user, initial, ch, i->first, whoresults);
291                         }
292                 }
293         }
294         else
295         {
296                 /* Match against wildcard of nick, server or host */
297                 if (opt_viewopersonly)
298                 {
299                         /* Showing only opers */
300                         for (std::list<User*>::iterator i = ServerInstance->Users->all_opers.begin(); i != ServerInstance->Users->all_opers.end(); i++)
301                         {
302                                 User* oper = *i;
303
304                                 if (whomatch(oper, matchtext))
305                                 {
306                                         if (!user->SharesChannelWith(oper))
307                                         {
308                                                 if (usingwildcards && (!oper->IsModeSet('i')) && (!IS_OPER(user)))
309                                                         continue;
310                                         }
311
312                                         SendWhoLine(user, initial, NULL, oper, whoresults);
313                                 }
314                         }
315                 }
316                 else
317                 {
318                         for (user_hash::iterator i = ServerInstance->Users->clientlist->begin(); i != ServerInstance->Users->clientlist->end(); i++)
319                         {
320                                 if (whomatch(i->second, matchtext))
321                                 {
322                                         if (!user->SharesChannelWith(i->second))
323                                         {
324                                                 if (usingwildcards && (i->second->IsModeSet('i')) && (!IS_OPER(user)))
325                                                         continue;
326                                         }
327
328                                         SendWhoLine(user, initial, NULL, i->second, whoresults);
329                                 }
330                         }
331                 }
332         }
333         /* Send the results out */
334         if ((ServerInstance->Config->MaxWhoResults && (whoresults.size() <= (size_t)ServerInstance->Config->MaxWhoResults)) || opt_unlimit)
335         {
336                 for (std::vector<std::string>::const_iterator n = whoresults.begin(); n != whoresults.end(); n++)
337                         user->WriteServ(*n);
338                 user->WriteNumeric(315, "%s %s :End of /WHO list.",user->nick, *parameters[0].c_str() ? parameters[0].c_str() : "*");
339                 return CMD_SUCCESS;
340         }
341         else
342         {
343                 /* BZZT! Too many results. */
344                 user->WriteNumeric(315, "%s %s :Too many results",user->nick, parameters[0].c_str());
345                 return CMD_FAILURE;
346         }
347 }
348