Doxyfy whocmds.h and whocmds.c.
[ircu2.10.12-pk.git] / ircd / whocmds.c
1 /*
2  * IRC - Internet Relay Chat, ircd/s_user.c (formerly ircd/s_msg.c)
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 /* @file
24  * @brief Support functions for /WHO-like commands.
25  * @version $Id$
26  */
27 #include "config.h"
28
29 #include "whocmds.h"
30 #include "channel.h"
31 #include "client.h"
32 #include "hash.h"
33 #include "ircd.h"
34 #include "ircd_chattr.h"
35 #include "ircd_features.h"
36 #include "ircd_reply.h"
37 #include "ircd_snprintf.h"
38 #include "ircd_string.h"
39 #include "list.h"
40 #include "match.h"
41 #include "numeric.h"
42 #include "numnicks.h"
43 #include "querycmds.h"
44 #include "random.h"
45 #include "s_bsd.h"
46 #include "s_conf.h"
47 #include "s_misc.h"
48 #include "s_user.h"
49 #include "send.h"
50 #include "struct.h"
51 #include "sys.h"
52 #include "userload.h"
53 #include "version.h"
54 #include "whowas.h"
55 #include "msg.h"
56
57 #include <arpa/inet.h>        /* inet_ntoa */
58 #include <fcntl.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <sys/stat.h>
63 #include <unistd.h>
64
65 /** Send a WHO reply to a client who asked.
66  * @param[in] sptr Client who is searching for other users.
67  * @param[in] acptr Client who may be shown to \a sptr.
68  * @param[in] repchan Shared channel that provides visibility.
69  * @param[in] fields Bitmask of WHO_FIELD_* values, indicating what to show.
70  * @param[in] qrt Query type string (ignored unless \a fields & WHO_FIELD_QTY).
71  */
72 void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan,
73             int fields, char* qrt)
74 {
75   char *p1;
76   struct Channel *chptr = repchan;
77
78   static char buf1[512];
79   /* NOTE: with current fields list and sizes this _cannot_ overrun, 
80      and also the message finally sent shouldn't ever be truncated */
81
82   p1 = buf1;
83   buf1[1] = '\0';
84
85   /* If we don't have a channel and we need one... try to find it,
86      unless the listing is for a channel service, we already know
87      that there are no common channels, thus use PubChannel and not
88      SeeChannel */
89   if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) &&
90       !IsChannelService(acptr))
91   {
92     struct Membership* chan;
93     for (chan = cli_user(acptr)->channel; chan && !chptr; chan = chan->next_channel)
94       if (PubChannel(chan->channel) &&
95           (acptr == sptr || !IsZombie(chan)))
96         chptr = chan->channel;
97   }
98
99   /* Place the fields one by one in the buffer and send it
100      note that fields == NULL means "default query" */
101
102   if (fields & WHO_FIELD_QTY)   /* Query type */
103   {
104     *(p1++) = ' ';
105     if (BadPtr(qrt))
106       *(p1++) = '0';
107     else
108       while ((*qrt) && (*(p1++) = *(qrt++)));
109   }
110
111   if (!fields || (fields & WHO_FIELD_CHA))
112   {
113     char *p2;
114     *(p1++) = ' ';
115     if ((p2 = (chptr ? chptr->chname : NULL)))
116       while ((*p2) && (*(p1++) = *(p2++)));
117     else
118       *(p1++) = '*';
119   }
120
121   if (!fields || (fields & WHO_FIELD_UID))
122   {
123     char *p2 = cli_user(acptr)->username;
124     *(p1++) = ' ';
125     while ((*p2) && (*(p1++) = *(p2++)));
126   }
127
128   if (fields & WHO_FIELD_NIP)
129   {
130     const char* p2 = HasHiddenHost(acptr) && !IsAnOper(sptr) ?
131       feature_str(FEAT_HIDDEN_IP) :
132       ircd_ntoa(&cli_ip(acptr));
133     *(p1++) = ' ';
134     while ((*p2) && (*(p1++) = *(p2++)));
135   }
136
137   if (!fields || (fields & WHO_FIELD_HOS))
138   {
139     char *p2 = cli_user(acptr)->host;
140     *(p1++) = ' ';
141     while ((*p2) && (*(p1++) = *(p2++)));
142   }
143
144   if (!fields || (fields & WHO_FIELD_SER))
145   {
146     const char *p2 = (feature_bool(FEAT_HIS_WHO_SERVERNAME) && !IsAnOper(sptr)) ?
147                        feature_str(FEAT_HIS_SERVERNAME) :
148                        cli_name(cli_user(acptr)->server);
149     *(p1++) = ' ';
150     while ((*p2) && (*(p1++) = *(p2++)));
151   }
152
153   if (!fields || (fields & WHO_FIELD_NIC))
154   {
155     char *p2 = cli_name(acptr);
156     *(p1++) = ' ';
157     while ((*p2) && (*(p1++) = *(p2++)));
158   }
159
160   if (!fields || (fields & WHO_FIELD_FLA))
161   {
162     *(p1++) = ' ';
163     if (cli_user(acptr)->away)
164       *(p1++) = 'G';
165     else
166       *(p1++) = 'H';
167     if SeeOper(sptr,acptr)
168       *(p1++) = '*';
169     if (fields) {
170       /* If you specified flags then we assume you know how to parse
171        * multiple channel status flags, as this is currently the only
172        * way to know if someone has @'s *and* is +'d.
173        */
174       if (chptr && is_chan_op(acptr, chptr))
175         *(p1++) = '@';
176       if (chptr && has_voice(acptr, chptr))
177         *(p1++) = '+';
178       if (chptr && is_zombie(acptr, chptr))
179         *(p1++) = '!';
180     }
181     else {
182       if (chptr && is_chan_op(acptr, chptr))
183         *(p1++) = '@';
184       else if (chptr && has_voice(acptr, chptr))
185         *(p1++) = '+';
186       else if (chptr && is_zombie(acptr, chptr))
187         *(p1++) = '!';
188     }
189     if (IsDeaf(acptr))
190       *(p1++) = 'd';
191     if (IsAnOper(sptr))
192     {
193       if (IsInvisible(acptr))
194         *(p1++) = 'i';
195       if (SendWallops(acptr))
196         *(p1++) = 'w';
197       if (SendDebug(acptr))
198         *(p1++) = 'g';
199     }
200     if (HasHiddenHost(acptr))
201       *(p1++) = 'x';
202   }
203
204   if (!fields || (fields & WHO_FIELD_DIS))
205   {
206     *p1++ = ' ';
207     if (!fields)
208       *p1++ = ':';              /* Place colon here for default reply */
209     if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr))
210       *p1++ = (sptr == acptr) ? '0' : '3';
211     else
212       /* three digit hopcount maximum */
213       p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr));
214   }
215
216   if (fields & WHO_FIELD_IDL)
217   {
218     *p1++ = ' ';
219     if (MyUser(acptr) &&
220         (IsAnOper(sptr) || !feature_bool(FEAT_HIS_WHO_SERVERNAME) ||
221          acptr == sptr))
222       p1 += ircd_snprintf(0, p1, 11, "%d",
223                           CurrentTime - cli_user(acptr)->last);
224     else
225       *p1++ = '0';
226   }
227
228   if (fields & WHO_FIELD_ACC)
229   {
230     char *p2 = cli_user(acptr)->account;
231     *(p1++) = ' ';
232     if (*p2)
233       while ((*p2) && (*(p1++) = *(p2++)));
234     else
235       *(p1++) = '0';
236   }
237
238   if (!fields || (fields & WHO_FIELD_REN))
239   {
240     char *p2 = cli_info(acptr);
241     *p1++ = ' ';
242     if (fields)
243       *p1++ = ':';              /* Place colon here for special reply */
244     while ((*p2) && (*(p1++) = *(p2++)));
245   }
246
247   /* The first char will always be an useless blank and we 
248      need to terminate buf1 */
249   *p1 = '\0';
250   p1 = buf1;
251   send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1);
252 }
253
254 /** Count number of users who match \a mask.
255  * @param[in] mask user\@host or user\@ip mask to check.
256  * @return Count of matching users.
257  */
258 int
259 count_users(char *mask)
260 {
261   struct Client *acptr;
262   int count = 0;
263   char namebuf[USERLEN + HOSTLEN + 2];
264   char ipbuf[USERLEN + 16 + 2];
265
266   for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
267     if (!IsUser(acptr))
268       continue;
269
270     ircd_snprintf(0, namebuf, sizeof(namebuf), "%s@%s",
271                   cli_user(acptr)->username, cli_user(acptr)->host);
272     ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username,
273                   ircd_ntoa(&cli_ip(acptr)));
274
275     if (!match(mask, namebuf) || !match(mask, ipbuf))
276       count++;
277   }
278
279   return count;
280 }