]>
Commit | Line | Data |
---|---|---|
189935b1 | 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: whocmds.c,v 1.25 2005/09/27 03:59:55 entrope Exp $ | |
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 <fcntl.h> | |
58 | #include <stdio.h> | |
59 | #include <stdlib.h> | |
60 | #include <string.h> | |
61 | #include <sys/stat.h> | |
62 | #include <unistd.h> | |
63 | ||
64 | /** Send a WHO reply to a client who asked. | |
65 | * @param[in] sptr Client who is searching for other users. | |
66 | * @param[in] acptr Client who may be shown to \a sptr. | |
67 | * @param[in] repchan Shared channel that provides visibility. | |
68 | * @param[in] fields Bitmask of WHO_FIELD_* values, indicating what to show. | |
69 | * @param[in] qrt Query type string (ignored unless \a fields & WHO_FIELD_QTY). | |
70 | */ | |
71 | void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, | |
72 | int fields, char* qrt) | |
73 | { | |
74 | char *p1; | |
75 | struct Membership *chan = 0; | |
76 | ||
77 | static char buf1[512]; | |
78 | /* NOTE: with current fields list and sizes this _cannot_ overrun, | |
79 | and also the message finally sent shouldn't ever be truncated */ | |
80 | ||
81 | p1 = buf1; | |
82 | buf1[1] = '\0'; | |
83 | ||
84 | /* If we don't have a channel and we need one... try to find it, | |
85 | unless the listing is for a channel service, we already know | |
86 | that there are no common channels, thus use PubChannel and not | |
87 | SeeChannel */ | |
88 | if (repchan) | |
89 | chan = find_channel_member(acptr, repchan); | |
90 | else if ((!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) | |
91 | && !IsChannelService(acptr)) | |
92 | { | |
93 | for (chan = cli_user(acptr)->channel; chan; chan = chan->next_channel) | |
94 | if (PubChannel(chan->channel) && | |
95 | (acptr == sptr || !IsZombie(chan))) | |
96 | break; | |
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 = (chan ? chan->channel->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 (!chan) { | |
170 | /* No flags possible for the channel, so skip them all. */ | |
171 | } | |
172 | else if (fields) { | |
173 | /* If you specified flags then we assume you know how to parse | |
174 | * multiple channel status flags, as this is currently the only | |
175 | * way to know if someone has @'s *and* is +'d. | |
176 | */ | |
177 | if (IsChanOp(chan)) | |
178 | *(p1++) = '@'; | |
179 | if (HasVoice(chan)) | |
180 | *(p1++) = '+'; | |
181 | if (IsZombie(chan)) | |
182 | *(p1++) = '!'; | |
183 | if (IsDelayedJoin(chan)) | |
184 | *(p1++) = '<'; | |
185 | } | |
186 | else { | |
187 | if (IsChanOp(chan)) | |
188 | *(p1++) = '@'; | |
189 | else if (HasVoice(chan)) | |
190 | *(p1++) = '+'; | |
191 | else if (IsZombie(chan)) | |
192 | *(p1++) = '!'; | |
193 | else if (IsDelayedJoin(chan)) | |
194 | *(p1++) = '<'; | |
195 | } | |
196 | if (IsDeaf(acptr)) | |
197 | *(p1++) = 'd'; | |
198 | if (IsAnOper(sptr)) | |
199 | { | |
200 | if (IsInvisible(acptr)) | |
201 | *(p1++) = 'i'; | |
202 | if (SendWallops(acptr)) | |
203 | *(p1++) = 'w'; | |
204 | if (SendDebug(acptr)) | |
205 | *(p1++) = 'g'; | |
206 | } | |
207 | if (HasHiddenHost(acptr)) | |
208 | *(p1++) = 'x'; | |
209 | } | |
210 | ||
211 | if (!fields || (fields & WHO_FIELD_DIS)) | |
212 | { | |
213 | *p1++ = ' '; | |
214 | if (!fields) | |
215 | *p1++ = ':'; /* Place colon here for default reply */ | |
216 | if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr)) | |
217 | *p1++ = (sptr == acptr) ? '0' : '3'; | |
218 | else | |
219 | /* three digit hopcount maximum */ | |
220 | p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr)); | |
221 | } | |
222 | ||
223 | if (fields & WHO_FIELD_IDL) | |
224 | { | |
225 | *p1++ = ' '; | |
226 | if (MyUser(acptr) && | |
227 | (IsAnOper(sptr) || !feature_bool(FEAT_HIS_WHO_SERVERNAME) || | |
228 | acptr == sptr)) | |
229 | p1 += ircd_snprintf(0, p1, 11, "%d", | |
230 | CurrentTime - cli_user(acptr)->last); | |
231 | else | |
232 | *p1++ = '0'; | |
233 | } | |
234 | ||
235 | if (fields & WHO_FIELD_ACC) | |
236 | { | |
237 | char *p2 = cli_user(acptr)->account; | |
238 | *(p1++) = ' '; | |
239 | if (*p2) | |
240 | while ((*p2) && (*(p1++) = *(p2++))); | |
241 | else | |
242 | *(p1++) = '0'; | |
243 | } | |
244 | ||
245 | if (!fields || (fields & WHO_FIELD_REN)) | |
246 | { | |
247 | char *p2 = cli_info(acptr); | |
248 | *p1++ = ' '; | |
249 | if (fields) | |
250 | *p1++ = ':'; /* Place colon here for special reply */ | |
251 | while ((*p2) && (*(p1++) = *(p2++))); | |
252 | } | |
253 | ||
254 | /* The first char will always be an useless blank and we | |
255 | need to terminate buf1 */ | |
256 | *p1 = '\0'; | |
257 | p1 = buf1; | |
258 | send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1); | |
259 | } | |
260 | ||
261 | /** Count number of users who match \a mask. | |
262 | * @param[in] mask user\@host or user\@ip mask to check. | |
263 | * @return Count of matching users. | |
264 | */ | |
265 | int | |
266 | count_users(char *mask) | |
267 | { | |
268 | struct Client *acptr; | |
269 | int count = 0; | |
270 | char namebuf[USERLEN + HOSTLEN + 2]; | |
271 | char ipbuf[USERLEN + 16 + 2]; | |
272 | ||
273 | for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { | |
274 | if (!IsUser(acptr)) | |
275 | continue; | |
276 | ||
277 | ircd_snprintf(0, namebuf, sizeof(namebuf), "%s@%s", | |
278 | cli_user(acptr)->username, cli_user(acptr)->host); | |
279 | ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username, | |
280 | ircd_ntoa(&cli_ip(acptr))); | |
281 | ||
282 | if (!match(mask, namebuf) || !match(mask, ipbuf)) | |
283 | count++; | |
284 | } | |
285 | ||
286 | return count; | |
287 | } |