]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/m_check.c
Update my e-mail address.
[irc/quakenet/snircd.git] / ircd / m_check.c
CommitLineData
f4a888ca 1/*
2 * IRC - Internet Relay Chat, ircd/m_check.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
24#include "channel.h"
25#include "check.h"
cf49cc73 26#include "class.h"
f4a888ca 27#include "client.h"
28#include "hash.h"
29#include "ircd.h"
30#include "ircd_alloc.h"
31#include "ircd_defs.h"
32#include "ircd_features.h"
33#include "ircd_reply.h"
34#include "ircd_string.h"
35#include "ircd_snprintf.h"
caa12862 36#include "ircd_osdep.h"
f4a888ca 37#include "list.h"
38#include "listener.h"
39#include "match.h"
40#include "numeric.h"
41#include "numnicks.h"
42#include "querycmds.h"
43#include "send.h"
44#include "s_user.h"
45#include "s_debug.h"
cf49cc73 46#include "s_misc.h"
f4a888ca 47
48#include <string.h>
49
a11c9561 50#define CHECK_CHECKCHAN 0x01 /* -c */
51#define CHECK_SHOWUSERS 0x02 /* ! -u */
52#define CHECK_OPSONLY 0x04 /* -o */
53#define CHECK_SHOWIPS 0x08 /* -i */
54#define CHECK_CIDRMASK 0x10 /* automatically detected when performing a hostmask /CHECK */
55#define CHECK_OPLEVELS 0x20 /* -l */
56#define CHECK_CLONES 0x40 /* -C */
57#define CHECK_SHOWSERVER 0x80 /* -s */
58#define CHECK_SHOWHOSTIP 0x100 /* -I */
f4a888ca 59/*
60 * - ASUKA ---------------------------------------------------------------------
61 * This is the implimentation of the CHECK function for Asuka.
62 * Some of this code is from previous QuakeNet ircds, but most of it is mine..
63 * The old code was written by Durzel (durzel@quakenet.org).
64 *
65 * qoreQ (qoreQ@quakenet.org) - 08/14/2002
66 * -----------------------------------------------------------------------------
67 */
68
69/*
70 * Syntax: CHECK <channel|nick|server|hostmask> [-flags]
71 *
72 * Where valid flags are:
73 * -c: Show channels when checking a hostmask.
74 * -i: Show IPs instead of hostnames when displaying results.
75 * -o: Only show channel operators when checking a channel.
76 * -u: Hide users when checking a channel.
cf49cc73 77 * -l: Show oplevels when checking a channel.
78 * -C: Perform clone count when checking a channel.
a11c9561 79 * -s: show server user is on when checking a channel.
80 *
f4a888ca 81 * <hostmask> can be of the form host, user@host, nick!user@host,
82 * with host being host.domain.cc, 127.0.0.1 or 127.0.0.0/24.
83 * Wildcards are supported.
84 */
85
86int m_check(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) {
87 struct Channel *chptr;
88 struct Client *acptr;
f4a888ca 89 int flags = CHECK_SHOWUSERS, i;
90
a0200102 91 if (!HasPriv(sptr, PRIV_CHECK))
92 return send_reply(sptr, ERR_NOPRIVILEGES);
93
f4a888ca 94 if (parc < 2) {
95 send_reply(sptr, ERR_NEEDMOREPARAMS, "CHECK");
96 return 0;
97 }
98
99 /* This checks to see if any flags have been supplied */
100 if ((parc >= 3) && (parv[2][0] == '-')) {
101 for (i = 0; parv[2][i]; i++) {
102 switch (parv[2][i]) {
103 case 'c':
104 flags |= CHECK_CHECKCHAN;
105 break;
106
107 case 'o':
108 flags |= CHECK_OPSONLY; /* fall through */
109 case 'u':
110 flags &= ~(CHECK_SHOWUSERS);
111 break;
112
113 case 'i':
114 flags |= CHECK_SHOWIPS;
115 break;
cf49cc73 116 case 'l':
117 flags |= CHECK_OPLEVELS;
118 break;
119 case 'C':
120 flags |= CHECK_CLONES;
121 break;
a11c9561 122 case 's':
123 flags |= CHECK_SHOWSERVER;
124 break;
125 case 'I':
126 flags |= CHECK_SHOWHOSTIP;
127 break;
f4a888ca 128 default:
129 /* might want to raise some sort of error here? */
130 break;
131 }
132 }
133 }
134
135 if (IsChannelName(parv[1])) { /* channel */
136 if ((chptr = FindChannel(parv[1]))) {
137 checkChannel(sptr, chptr);
138 checkUsers(sptr, chptr, flags);
139 }
140 else
141 send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
142 }
143 else if ((acptr = FindClient(parv[1])) && !(FindServer(parv[1]))) { /* client and not a server */
144 if (!IsRegistered(acptr)) {
145 send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
146 return 0;
147 }
148
149 checkClient(sptr, acptr);
150 }
151 else if ((acptr = FindServer(parv[1]))) { /* server */
152 checkServer(sptr, acptr);
153 }
154 else if (checkHostmask(sptr, parv[1], flags) > 0) /* hostmask */
155 return 1;
156 else /* no match */
157 send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
158
159 return 1;
160}
161
cf49cc73 162static int checkClones(struct Channel *chptr, struct Client *cptr) {
163 int clones = 0, count = 0;
f4a888ca 164 struct Membership *lp;
165 struct Client *acptr;
166
167 for (lp = chptr->members; lp; lp = lp->next_member) {
168 acptr = lp->user;
cf49cc73 169 if (!strcmp(cli_user(cptr)->realhost, cli_user(acptr)->realhost)) {
f4a888ca 170 clones++;
171 }
172 }
173
cf49cc73 174 /* Optimise only if we will actually save CPU time */
175 if (clones >= 2) {
176 for (lp = chptr->members; lp; lp = lp->next_member) {
177 acptr = lp->user;
178 if (!strcmp(cli_user(cptr)->realhost, cli_user(acptr)->realhost)) {
179 cli_marker(acptr) = clones;
180 count++;
181 if (clones == count) {
182 break;
183 }
184 }
185 }
186 }
187
188 return clones;
f4a888ca 189}
190
191void checkUsers(struct Client *sptr, struct Channel *chptr, int flags) {
192 struct Membership *lp;
193 struct Ban *ban;
194 struct Client *acptr;
195
a11c9561 196 char outbuf[BUFSIZE], outbuf2[BUFSIZE], ustat[64];
081bd606 197 int cntr = 0, opcntr = 0, vcntr = 0, clones = 0, bans = 0, authed = 0;
f4a888ca 198
cf49cc73 199 if (flags & CHECK_SHOWUSERS) {
200 send_reply(sptr, RPL_DATASTR, "Users (@ = op, + = voice)");
201 }
202
203 if (flags & CHECK_CLONES) {
204 for (lp = chptr->members; lp; lp = lp->next_member) {
205 cli_marker(lp->user) = 0;
206 }
207 }
f4a888ca 208
209 for (lp = chptr->members; lp; lp = lp->next_member) {
cf49cc73 210 int opped = 0, c = 0;
f4a888ca 211
212 acptr = lp->user;
213
cf49cc73 214 if (flags & CHECK_CLONES) {
215 if (!cli_marker(acptr)) {
216 c = checkClones(chptr, acptr);
217 } else {
218 c = cli_marker(acptr);
219 }
220
221 if (c != 1) {
222 clones++;
223 }
f4a888ca 224 }
f4a888ca 225
226 if (chptr && is_chan_op(acptr, chptr)) {
cf49cc73 227 if (flags & CHECK_OPLEVELS) {
228 if (c) {
a11c9561 229 ircd_snprintf(0, ustat, sizeof(ustat), "%2d %3hu@", c, OpLevel(lp));
cf49cc73 230 } else {
a11c9561 231 ircd_snprintf(0, ustat, sizeof(ustat), "%3hu@", OpLevel(lp));
cf49cc73 232 }
233 } else {
234 if (c) {
235 ircd_snprintf(0, ustat, sizeof(ustat), "%2d @", c);
236 } else {
237 ircd_snprintf(0, ustat, sizeof(ustat), "@");
238 }
239 }
f4a888ca 240 opcntr++;
241 opped = 1;
242 }
243 else if (chptr && has_voice(acptr, chptr)) {
cf49cc73 244 if (c) {
a11c9561 245 ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s+", c, (flags & CHECK_OPLEVELS) ? " " : "");
cf49cc73 246 } else {
a11c9561 247 ircd_snprintf(0, ustat, sizeof(ustat), "%s", (flags & CHECK_OPLEVELS) ? " +" : "+");
cf49cc73 248 }
f4a888ca 249 vcntr++;
250 }
cf49cc73 251 else {
252 if (c) {
a11c9561 253 ircd_snprintf(0, ustat, sizeof(ustat), "%2d %s", c, (flags & CHECK_OPLEVELS) ? " " : "" );
cf49cc73 254 } else {
a11c9561 255 ircd_snprintf(0, ustat, sizeof(ustat), " %s", (flags & CHECK_OPLEVELS) ? " " : "" );
cf49cc73 256 }
257 }
f4a888ca 258
cf49cc73 259 if ((c = IsAccount(acptr))) {
260 authed++;
261 }
f4a888ca 262
263 if ((flags & CHECK_SHOWUSERS) || ((flags & CHECK_OPSONLY) && opped)) {
264 ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%c", acptr->cli_info, COLOR_OFF);
a11c9561 265 if (flags & CHECK_SHOWHOSTIP ) {
266 ircd_snprintf(0, outbuf2, sizeof(outbuf2), " [%s]", ircd_ntoa(&(cli_ip(acptr))));
267 }
cf49cc73 268 send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, cli_user(acptr)->realusername,
a11c9561 269 ((flags & CHECK_SHOWIPS) ? ircd_ntoa(&(cli_ip(acptr))) : cli_user(acptr)->realhost), (flags & CHECK_SHOWHOSTIP) ? outbuf2 : "", (flags & CHECK_SHOWSERVER) ? cli_name(cli_user(acptr)->server) : outbuf,
cf49cc73 270 (c ? cli_user(acptr)->account : ""));
f4a888ca 271 }
272
273 cntr++;
274 }
275
276 send_reply(sptr, RPL_DATASTR, " ");
277
cf49cc73 278 if (flags & CHECK_CLONES) {
279 ircd_snprintf(0, outbuf, sizeof(outbuf),
280 "Total users:: %d (%d ops, %d voiced, %d clones, %d authed)",
281 cntr, opcntr, vcntr, clones, authed);
282 } else {
283 ircd_snprintf(0, outbuf, sizeof(outbuf),
284 "Total users:: %d (%d ops, %d voiced, %d authed)",
285 cntr, opcntr, vcntr, authed);
286 }
f4a888ca 287
288 send_reply(sptr, RPL_DATASTR, outbuf);
f4a888ca 289 send_reply(sptr, RPL_DATASTR, " ");
290
291 /* Do not display bans if ! flags & CHECK_SHOWUSERS */
cf49cc73 292 if (flags & CHECK_SHOWUSERS) {
293 send_reply(sptr, RPL_DATASTR, "Bans on channel::");
f4a888ca 294
cf49cc73 295 for (ban = chptr->banlist; ban; ban = ban->next) {
296 ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s - Set by %s, on %s",
297 ++bans, ban->banstr, ban->who, myctime(ban->when));
298 send_reply(sptr, RPL_DATASTR, outbuf);
299 }
f4a888ca 300
cf49cc73 301 if (bans == 0)
302 send_reply(sptr, RPL_DATASTR, "<none>");
f4a888ca 303 }
304
f4a888ca 305 send_reply(sptr, RPL_ENDOFCHECK, " ");
306}
307
308void checkChannel(struct Client *sptr, struct Channel *chptr) {
309 char outbuf[TOPICLEN + MODEBUFLEN + 64], modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];
310
311 /* Header */
312 send_reply(sptr, RPL_DATASTR, " ");
313 send_reply(sptr, RPL_CHKHEAD, "channel", chptr->chname);
314 send_reply(sptr, RPL_DATASTR, " ");
315
316 /* Creation Time */
317 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Creation time:: %s", myctime(chptr->creationtime));
318 send_reply(sptr, RPL_DATASTR, outbuf);
319
320 /* Topic */
321 if (strlen(chptr->topic) <= 0)
322 send_reply(sptr, RPL_DATASTR, " Topic:: <none>");
323 else {
324 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Topic:: %s", chptr->topic);
325 send_reply(sptr, RPL_DATASTR, outbuf);
326
327 /* ..set by */
328 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set by:: %s", chptr->topic_nick);
329 send_reply(sptr, RPL_DATASTR, outbuf);
cf49cc73 330
331 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set at:: %s", myctime(chptr->topic_time));
332 send_reply(sptr, RPL_DATASTR, outbuf);
f4a888ca 333 }
334
335 /* Channel Modes */
336
337 strcpy(outbuf, "Channel mode(s):: ");
338
339 modebuf[0] = '\0';
340 parabuf[0] = '\0';
341
342 channel_modes(sptr, modebuf, parabuf, sizeof(modebuf), chptr, NULL);
343
344 if(modebuf[1] == '\0')
345 strcat(outbuf, "<none>");
346 else if(*parabuf) {
347 strcat(outbuf, modebuf);
348 strcat(outbuf, " ");
349 strcat(outbuf, parabuf);
350 }
351 else
352 strcat(outbuf, modebuf);
353
354 send_reply(sptr, RPL_DATASTR, outbuf);
355
356 /* Don't send 'END OF CHECK' message, it's sent in checkUsers, which is called after this. */
357}
358
359void checkClient(struct Client *sptr, struct Client *acptr) {
360 struct Channel *chptr;
361 struct Membership *lp;
362 struct irc_sockaddr sin;
363 char outbuf[BUFSIZE];
49f8e96a 364 char *umodes;
f4a888ca 365 time_t nowr;
366
367 /* Header */
368 send_reply(sptr, RPL_DATASTR, " ");
cf49cc73 369 send_reply(sptr, RPL_CHKHEAD, "user", cli_name(acptr));
f4a888ca 370 send_reply(sptr, RPL_DATASTR, " ");
371
cf49cc73 372 ircd_snprintf(0, outbuf, sizeof(outbuf), " Nick:: %s (%s%s)", cli_name(acptr), NumNick(acptr));
f4a888ca 373 send_reply(sptr, RPL_DATASTR, outbuf);
374
375 if (MyUser(acptr)) {
376 ircd_snprintf(0, outbuf, sizeof(outbuf), " Signed on:: %s", myctime(acptr->cli_firsttime));
377 send_reply(sptr, RPL_DATASTR, outbuf);
378 }
379
380 ircd_snprintf(0, outbuf, sizeof(outbuf), " Timestamp:: %s (%d)", myctime(acptr->cli_lastnick), acptr->cli_lastnick);
381 send_reply(sptr, RPL_DATASTR, outbuf);
382
cf49cc73 383 ircd_snprintf(0, outbuf, sizeof(outbuf), " User/Hostmask:: %s@%s (%s)", cli_user(acptr)->username, cli_user(acptr)->host,
f4a888ca 384 ircd_ntoa(&(cli_ip(acptr))));
385 send_reply(sptr, RPL_DATASTR, outbuf);
386
387 if (IsSetHost(acptr) || IsAccount(acptr)) {
cf49cc73 388 ircd_snprintf(0, outbuf, sizeof(outbuf), " Real User/Host:: %s@%s", cli_user(acptr)->realusername, cli_user(acptr)->realhost);
f4a888ca 389 send_reply(sptr, RPL_DATASTR, outbuf);
390 }
391
392 ircd_snprintf(0, outbuf, sizeof(outbuf), " Real Name:: %s%c", cli_info(acptr), COLOR_OFF);
393 send_reply(sptr, RPL_DATASTR, outbuf);
394
897a5acc 395 if (IsChannelService(acptr) && IsService(cli_user(acptr)->server))
f4a888ca 396 send_reply(sptr, RPL_DATASTR, " Status:: Network Service");
397 else if (IsAnOper(acptr))
398 send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator");
399
cf49cc73 400 ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected to:: %s (%d)", cli_name(cli_user(acptr)->server), cli_hopcount(acptr));
f4a888ca 401 send_reply(sptr, RPL_DATASTR, outbuf);
402
403 /* +s (SERV_NOTICE) is not relayed to us from remote servers,
404 * so we cannot tell if a remote client has that mode set.
405 * And hacking it onto the end of the output of umode_str is EVIL BAD AND WRONG
406 * (and breaks if the user is +r) so we won't do that either.
407 */
408
49f8e96a 409 umodes = umode_str(acptr, 1);
410 if (umodes[0] == '+')
f4a888ca 411 strcpy(outbuf, " Umode(s):: <none>");
412 else
49f8e96a 413 ircd_snprintf(0, outbuf, sizeof(outbuf), " Umode(s):: +%s", umodes);
f4a888ca 414 send_reply(sptr, RPL_DATASTR, outbuf);
415
cf49cc73 416 if (cli_user(acptr)->joined == 0)
f4a888ca 417 send_reply(sptr, RPL_DATASTR, " Channel(s):: <none>");
cf49cc73 418 else if (cli_user(acptr)->joined > 50) {
f4a888ca 419
420 /* NB. As a sanity check, we DO NOT show the individual channels the
421 * client is on if it is on > 50 channels. This is to prevent the ircd
422 * barfing ala Uworld when someone does /quote check Q :).. (I shouldn't imagine
423 * an Oper would want to see every single channel 'x' client is on anyway if
424 * they are on *that* many).
425 */
426
cf49cc73 427 ircd_snprintf(0, outbuf, sizeof(outbuf), " Channel(s):: - (total: %u)", cli_user(acptr)->joined);
f4a888ca 428 send_reply(sptr, RPL_DATASTR, outbuf);
429 }
430 else {
431 char chntext[BUFSIZE];
432 int len = strlen(" Channel(s):: ");
cf49cc73 433 int mlen = strlen(me.cli_name) + len + strlen(cli_name(sptr));
f4a888ca 434 *chntext = '\0';
435
436 strcpy(chntext, " Channel(s):: ");
cf49cc73 437 for (lp = cli_user(acptr)->channel; lp; lp = lp->next_channel) {
f4a888ca 438 chptr = lp->channel;
439 if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) {
440 send_reply(sptr, RPL_DATASTR, chntext);
441 *chntext = '\0';
442 strcpy(chntext, " Channel(s):: ");
443 len = strlen(chntext);
444 }
445 if (IsDeaf(acptr))
446 *(chntext + len++) = '-';
447 if (is_chan_op(acptr, chptr))
448 *(chntext + len++) = '@';
449 else if (has_voice(acptr, chptr))
450 *(chntext + len++) = '+';
451 else if (IsZombie(lp))
452 *(chntext + len++) = '!';
cf49cc73 453 else if (IsDelayedJoin(lp))
454 *(chntext + len++) = '<';
f4a888ca 455 if (len)
456 *(chntext + len) = '\0';
457
458 strcpy(chntext + len, chptr->chname);
459 len += strlen(chptr->chname);
460 strcat(chntext + len, " ");
461 len++;
462 }
463
464 if (chntext[0] != '\0')
465 send_reply(sptr, RPL_DATASTR, chntext);
466 }
467
468 /* If client processing command ISN'T target (or a registered
469 * Network Service), show idle time since the last time we
470 * parsed something.
471 */
472 if (MyUser(acptr) && !(IsService(acptr) == -1) && !(strCasediff(acptr->cli_name, sptr->cli_name) == 0)) {
cf49cc73 473 nowr = CurrentTime - cli_user(acptr)->last;
f4a888ca 474 ircd_snprintf(0, outbuf, sizeof(outbuf), " Idle for:: %d days, %02ld:%02ld:%02ld",
475 nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
476 send_reply(sptr, RPL_DATASTR, outbuf);
477 }
478
479 /* Away message (if applicable) */
cf49cc73 480 if (cli_user(acptr)->away) {
481 ircd_snprintf(0, outbuf, sizeof(outbuf), " Away message:: %s", cli_user(acptr)->away);
f4a888ca 482 send_reply(sptr, RPL_DATASTR, outbuf);
483 }
484
485 /* If local user.. */
486 if (MyUser(acptr)) {
487 os_get_peername(con_fd(cli_connect(sptr)), &sin);
488
489 send_reply(sptr, RPL_DATASTR, " ");
490 ircd_snprintf(0, outbuf, sizeof(outbuf), " Ports:: %d -> %d (client -> server)",
491 sin.port, cli_listener(acptr)->addr.port);
492 send_reply(sptr, RPL_DATASTR, outbuf);
493 if (feature_bool(FEAT_EXTENDED_CHECKCMD)) {
494 /* Note: sendq = receiveq for a client (it makes sense really) */
495 ircd_snprintf(0, outbuf, sizeof(outbuf), " Data sent:: %lu.%0.3u Kb (%u protocol messages)",
496 (unsigned long)cli_receiveB(acptr) / 1024, (unsigned long)cli_receiveB(acptr) % 1024, cli_receiveM(acptr));
497 send_reply(sptr, RPL_DATASTR, outbuf);
498 ircd_snprintf(0, outbuf, sizeof(outbuf), " Data received:: %lu.%0.3lu Kb (%u protocol messages)",
499 (unsigned long)cli_sendB(acptr) / 1024, (unsigned long)cli_sendB(acptr) % 1024, cli_sendM(acptr));
500 send_reply(sptr, RPL_DATASTR, outbuf);
501 ircd_snprintf(0, outbuf, sizeof(outbuf), " receiveQ size:: %d bytes (max. %d bytes)",
502 DBufLength(&(cli_recvQ(acptr))), feature_int(FEAT_CLIENT_FLOOD));
503 send_reply(sptr, RPL_DATASTR, outbuf);
504 ircd_snprintf(0, outbuf, sizeof(outbuf), " sendQ size:: %d bytes (max. %d bytes)",
505 DBufLength(&(cli_sendQ(acptr))), get_sendq(acptr));
506 send_reply(sptr, RPL_DATASTR, outbuf);
507 }
508 }
509
510 /* Send 'END OF CHECK' message */
511 send_reply(sptr, RPL_ENDOFCHECK, " ");
512}
513
514void checkServer(struct Client *sptr, struct Client *acptr) {
515 char outbuf[BUFSIZE];
516
517 /* Header */
518 send_reply(sptr, RPL_DATASTR, " ");
519 send_reply(sptr, RPL_CHKHEAD, "server", acptr->cli_name);
520 send_reply(sptr, RPL_DATASTR, " ");
521
522 ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected at:: %s", myctime(acptr->cli_serv->timestamp));
523 send_reply(sptr, RPL_DATASTR, outbuf);
524
525 ircd_snprintf(0, outbuf, sizeof(outbuf), " Server name:: %s", acptr->cli_name);
526 send_reply(sptr, RPL_DATASTR, outbuf);
527
528 ircd_snprintf(0, outbuf, sizeof(outbuf), " Numeric:: %s --> %d", NumServ(acptr), base64toint(acptr->cli_yxx));
529 send_reply(sptr, RPL_DATASTR, outbuf);
530
9826eca0 531 ircd_snprintf(0, outbuf, sizeof(outbuf), " Users:: %d / %d", (acptr == &me) ? UserStats.local_clients : cli_serv(acptr)->clients,
f4a888ca 532 base64toint(cli_serv(acptr)->nn_capacity));
533 send_reply(sptr, RPL_DATASTR, outbuf);
534
535 if (IsBurst(acptr))
536 send_reply(sptr, RPL_DATASTR, " Status:: Bursting");
537 else if (IsBurstAck(acptr))
538 send_reply(sptr, RPL_DATASTR, " Status:: Awaiting EOB Ack");
539 else if (IsService(acptr))
540 send_reply(sptr, RPL_DATASTR, " Status:: Network Service");
541 else if (IsHub(acptr))
542 send_reply(sptr, RPL_DATASTR, " Status:: Network Hub");
543
544 if (feature_bool(FEAT_EXTENDED_CHECKCMD)) {
545 int dlinkc = 0;
546 struct DLink* slink = NULL;
547
548 send_reply(sptr, RPL_DATASTR, " ");
549 send_reply(sptr, RPL_DATASTR, "Downlinks::");
550 for (slink = cli_serv(acptr)->down; slink; slink = slink->next) {
551 ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s%s", ++dlinkc,
552 IsBurst(slink->value.cptr) ? "*" : IsBurstAck(slink->value.cptr) ? "!" : IsService(slink->value.cptr) ? "=" : IsHub(slink->value.cptr) ? "+" : " ",
553 cli_name(slink->value.cptr));
554 send_reply(sptr, RPL_DATASTR, outbuf);
555 }
556
557 if (!dlinkc)
558 send_reply(sptr, RPL_DATASTR, "<none>");
559 }
560
561 /* Send 'END OF CHECK' message */
562 send_reply(sptr, RPL_ENDOFCHECK, " ");
563}
564
565signed int checkHostmask(struct Client *sptr, char *hoststr, int flags) {
566 struct Client *acptr;
567 struct Channel *chptr;
568 struct Membership *lp;
569 int count = 0, found = 0;
570 char outbuf[BUFSIZE];
571 char targhost[NICKLEN + USERLEN + HOSTLEN + 3], curhost[NICKLEN + USERLEN + HOSTLEN + 3];
572 char nickm[NICKLEN + 1], userm[USERLEN + 1], hostm[HOSTLEN + 1];
573 char *p = NULL;
574 struct irc_in_addr cidr_check;
cf49cc73 575 unsigned char cidr_check_bits;
f4a888ca 576
577 strcpy(nickm,"*");
578 strcpy(userm,"*");
579 strcpy(hostm,"*");
580
581 if (!strchr(hoststr, '!') && !strchr(hoststr, '@'))
582 ircd_strncpy(hostm,hoststr,HOSTLEN);
583 else {
cf49cc73 584 if ((p = strchr(hoststr, '@'))) {
f4a888ca 585 *p++ = '\0';
586 if (*p) ircd_strncpy(hostm,p, HOSTLEN);
587 }
588
589 /* Get the nick!user mask */
cf49cc73 590 if ((p = strchr(hoststr, '!'))) {
f4a888ca 591 *p++ = '\0';
592 if (*p) ircd_strncpy(userm,p,USERLEN);
593 if (*hoststr) ircd_strncpy(nickm,hoststr,NICKLEN);
594 }
595 else if (*hoststr) {
596 /* Durz: We should only do the following *IF* the hoststr has not already been
597 * copied into hostm (ie. neither ! or @ specified).. otherwise, when we do
598 * /quote check *.barrysworld.com - we end up with targhost as: *!*.barryswo@*.barrysworld.com
599 */
600 ircd_strncpy(userm,hoststr,USERLEN);
601 }
602 }
603
604 if (ipmask_parse(hostm, &cidr_check, &cidr_check_bits) != 0) {
605 flags |= CHECK_CIDRMASK;
606 }
f4a888ca 607
608 /* Copy formatted string into "targhost" buffer */
609 ircd_snprintf(0, targhost, sizeof(targhost), "%s!%s@%s", nickm, userm, hostm);
610
611 targhost[sizeof(targhost) - 1] = '\0';
612
613 /* Note: we have to exclude the last client struct as it is not a real client
614 * structure, and therefore any attempt to access elements in it would cause
615 * a segfault.
616 */
617
618 for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
619 /* Dont process if acptr is a unregistered client, a server or a ping */
620 if (!IsRegistered(acptr) || IsServer(acptr))
621 continue;
622
623 if (IsMe(acptr)) /* Always the last acptr record */
624 break;
625
626 if(count > 500) { /* sanity stuff */
627 send_reply(sptr, RPL_ENDOFCHECK, " ");
628 break;
629 }
630
631 /* Copy host info into buffer */
632 curhost[0] = '\0';
cf49cc73 633 ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", cli_name(acptr), cli_user(acptr)->realusername, cli_user(acptr)->realhost);
f4a888ca 634
635 if (flags & CHECK_CIDRMASK) {
636 if (ipmask_check(&cli_ip(acptr), &cidr_check, cidr_check_bits) && !match(nickm, acptr->cli_name)
cf49cc73 637 && (!match(userm, cli_user(acptr)->realusername) || !match(userm, cli_user(acptr)->username)))
f4a888ca 638 found = 1;
639 }
640 else {
641 if(match((const char*)targhost,(const char*)curhost) == 0)
642 found = 1;
643 else {
644 curhost[0] = '\0';
cf49cc73 645 ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", acptr->cli_name, cli_user(acptr)->username, cli_user(acptr)->host);
f4a888ca 646
647 if(match((const char*)targhost,(const char*)curhost) == 0)
648 found = 1;
649 }
650 }
651
652 if (found == 1) {
653 found = 0; /* reset that so it doesn't get crazy go nuts */
654
655 /* Show header if we've found at least 1 record */
656 if (count == 0) {
657 /* Output header */
658 send_reply(sptr, RPL_DATASTR, " ");
659 send_reply(sptr, RPL_CHKHEAD, "host", targhost);
660
661 send_reply(sptr, RPL_DATASTR, " ");
662 ircd_snprintf(0, outbuf, sizeof(outbuf), "%s %-*s%-*s%s", "No.", (NICKLEN + 2 ), "Nick",
663 (USERLEN + 2), "User", "Host");
664 send_reply(sptr, RPL_DATASTR, outbuf);
665 }
666
667 ircd_snprintf(0, outbuf, sizeof(outbuf), "%-4d %-*s%-*s%s", (count+1), (NICKLEN + 2),
cf49cc73 668 acptr->cli_name, (USERLEN + 2), cli_user(acptr)->realusername,
669 (flags & CHECK_SHOWIPS) ? ircd_ntoa(&(cli_ip(acptr))) : cli_user(acptr)->realhost);
f4a888ca 670 send_reply(sptr, RPL_DATASTR, outbuf);
671
672 /* Show channel output (if applicable) - the 50 channel limit sanity check
673 * is specifically to prevent coredumping when someone lamely tries to /check
674 * Q or some other channel service...
675 */
676 if (flags & CHECK_CHECKCHAN) {
cf49cc73 677 if (cli_user(acptr)->joined > 0 && cli_user(acptr)->joined <= 50) {
f4a888ca 678 char chntext[BUFSIZE];
679 int len = strlen(" on channels: ");
680 int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name);
681 *chntext = '\0';
682
683 strcpy(chntext, " on channels: ");
cf49cc73 684 for (lp = cli_user(acptr)->channel; lp; lp = lp->next_channel) {
f4a888ca 685 chptr = lp->channel;
686 if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) {
687 send_reply(sptr, RPL_DATASTR, chntext);
688 *chntext = '\0';
689 strcpy(chntext, " on channels: ");
690 len = strlen(chntext);
691 }
692 if (IsDeaf(acptr))
693 *(chntext + len++) = '-';
694 if (is_chan_op(acptr, chptr))
695 *(chntext + len++) = '@';
696 else if (has_voice(acptr, chptr))
697 *(chntext + len++) = '+';
698 else if (IsZombie(lp))
699 *(chntext + len++) = '!';
cf49cc73 700 else if (IsDelayedJoin(lp))
701 *(chntext + len++) = '<';
f4a888ca 702 if (len)
703 *(chntext + len) = '\0';
704
705 strcpy(chntext + len, chptr->chname);
706 len += strlen(chptr->chname);
707 strcat(chntext + len, " ");
708 len++;
709 }
710 if (chntext[0] != '\0')
711 send_reply(sptr, RPL_DATASTR, chntext);
712
713 send_reply(sptr, RPL_DATASTR, " ");
714 }
715 }
716 count++;
717 }
718 }
719
720 if (count > 0) {
721 send_reply(sptr, RPL_DATASTR, " ");
722
723 ircd_snprintf(0, outbuf, sizeof(outbuf), "Matching records found:: %d", count);
724 send_reply(sptr, RPL_DATASTR, outbuf);
725
726 send_reply(sptr, RPL_ENDOFCHECK, " ");
727 }
728
729 return count;
730}