2 * IRC - Internet Relay Chat, ircd/m_check.c
3 * Written by David Herrmann.
7 * m_functions execute protocol messages on this server:
9 * cptr is always NON-NULL, pointing to a *LOCAL* client
10 * structure (with an open socket connected!). This
11 * identifies the physical socket where the message
12 * originated (or which caused the m_function to be
13 * executed--some m_functions may call others...).
15 * sptr is the source of the message, defined by the
16 * prefix part of the message if present. If not
17 * or prefix not found, then sptr==cptr.
19 * (!IsServer(cptr)) => (cptr == sptr), because
20 * prefixes are taken *only* from servers...
23 * (sptr == cptr) => the message didn't
26 * (sptr != cptr && IsServer(sptr) means
27 * the prefix specified servername. (?)
29 * (sptr != cptr && !IsServer(sptr) means
30 * that message originated from a remote
35 * (!IsServer(sptr)) means that, sptr can safely
36 * taken as defining the target structure of the
37 * message in this server.
39 * *Always* true (if 'parse' and others are working correct):
41 * 1) sptr->from == cptr (note: cptr->from == cptr)
43 * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
44 * *cannot* be a local connection, unless it's
45 * actually cptr!). [MyConnect(x) should probably
46 * be defined as (x == x->from) --msa ]
48 * parc number of variable parameter strings (if zero,
49 * parv is allowed to be NULL)
51 * parv a NULL terminated list of parameter pointers,
53 * parv[0], sender (prefix string), if not present
54 * this points to an empty string.
55 * parv[1]...parv[parc-1]
56 * pointers to additional parameters
57 * parv[parc] == NULL, *always*
59 * note: it is guaranteed that parv[0]..parv[parc-1] are all
69 #include "ircd_alloc.h"
70 #include "ircd_defs.h"
71 #include "ircd_features.h"
73 #include "ircd_reply.h"
74 #include "ircd_snprintf.h"
75 #include "ircd_string.h"
82 #include "querycmds.h"
92 #include <arpa/inet.h>
94 #define COLOR_OFF '\017'
96 void checkChannel(struct Client *sptr, struct Channel *chptr);
97 void checkUsers(struct Client *sptr, struct Channel *chptr, int flags);
98 void checkClient(struct Client *sptr, struct Client *acptr, int flags);
99 void checkServer(struct Client *sptr, struct Client *acptr);
101 static int checkClones(struct Channel *chptr, char *nick, char *host) {
103 struct Membership *lp;
104 struct Client *acptr;
106 for(lp = chptr->members; lp; lp = lp->next_member) {
108 if(!strcmp(acptr->cli_user->realhost, host) && strcmp(acptr->cli_name, nick)) clones++;
111 return ((clones) ? clones + 1 : 0);
114 /* This /check implementation is based on several other ircds. All of them
115 * are licensed under the GPL.
116 * The following comments are from each implementation.
120 * This is the implementation of the CHECK function for Asuka.
121 * Some of this code is from previous QuakeNet ircds, but most of it is mine..
122 * The old code was written by Durzel (durzel@quakenet.org).
124 * qoreQ (qoreQ@quakenet.org) - 08/14/2002
127 * Modified by falcon for ircu2.10.12-ircplanet
129 * Dominik Paulus - 2007/05/12
132 * Modified by gix for the IRCu-Patchset.
134 * David Herrmann - 2009/03/09
137 #define CHECK_CHECKCHAN 0x01 /* -c */
138 #define CHECK_SHOWUSERS 0x02 /* ! -u */
139 #define CHECK_OPSONLY 0x04 /* -o */
140 #define CHECK_SHOWIPS 0x08 /* -i */
143 * Syntax: CHECK <channel|nick|server> [-flags]
145 * Where valid flags are:
146 * -c: Show channels when checking a user even if the user is on more than 50 channels.
147 * -i: Show IPs instead of hostnames when displaying results.
148 * -o: Only show channel operators when checking a channel.
149 * -u: Hide users when checking a channel. Overrides -o.
153 * generic message handler
155 int mo_check(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) {
156 struct Channel *chptr;
157 struct Client *acptr;
158 int i, flags = CHECK_SHOWUSERS;
161 return send_reply(sptr, ERR_NOPRIVILEGES);
164 return send_reply(sptr, ERR_NEEDMOREPARAMS, "CHECK");
166 /* This checks to see if any flags have been supplied */
167 if((parc > 2) && (parv[2][0] == '-')) {
168 for(i = 1; parv[2][i]; ++i) {
169 switch (parv[2][i]) {
171 flags |= CHECK_CHECKCHAN;
174 if(flags & CHECK_SHOWUSERS)
175 flags |= CHECK_OPSONLY;
178 flags &= ~(CHECK_SHOWUSERS | CHECK_OPSONLY);
181 flags |= CHECK_SHOWIPS;
187 if((chptr = FindChannel(parv[1]))) {
188 checkChannel(sptr, chptr);
189 checkUsers(sptr, chptr, flags);
191 else if((acptr = FindUser(parv[1]))) {
192 if(!IsRegistered(acptr))
193 return send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
194 checkClient(sptr, acptr, flags);
196 else if((acptr = FindServer(parv[1])))
197 checkServer(sptr, acptr);
198 else send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]);
203 void checkServer(struct Client *sptr, struct Client *acptr) {
204 char outbuf[BUFSIZE];
206 struct DLink* slink = NULL;
209 send_reply(sptr, RPL_CHKHEAD, "server", acptr->cli_name);
210 send_reply(sptr, RPL_DATASTR, " ");
212 ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected at: %s", myctime(acptr->cli_serv->timestamp));
213 send_reply(sptr, RPL_DATASTR, outbuf);
215 ircd_snprintf(0, outbuf, sizeof(outbuf), " Server name: %s", acptr->cli_name);
216 send_reply(sptr, RPL_DATASTR, outbuf);
218 ircd_snprintf(0, outbuf, sizeof(outbuf), " Numeric: %s --> %d", NumServ(acptr), base64toint(acptr->cli_yxx));
219 send_reply(sptr, RPL_DATASTR, outbuf);
221 ircd_snprintf(0, outbuf, sizeof(outbuf), " Users: %d / %d", cli_serv(acptr)->clients, base64toint(cli_serv(acptr)->nn_capacity));
222 send_reply(sptr, RPL_DATASTR, outbuf);
225 send_reply(sptr, RPL_DATASTR, " Status: Bursting");
226 if(IsBurstAck(acptr))
227 send_reply(sptr, RPL_DATASTR, " Status: Awaiting EOB Ack");
229 send_reply(sptr, RPL_DATASTR, " Status: Network Service");
231 send_reply(sptr, RPL_DATASTR, " Status: Network Hub");
233 send_reply(sptr, RPL_DATASTR, " Status: Network Leaf");
235 send_reply(sptr, RPL_DATASTR, " ");
236 send_reply(sptr, RPL_DATASTR, "Downlinks:");
237 for(slink = cli_serv(acptr)->down; slink; slink = slink->next) {
238 ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s%s", ++dlinkc,
239 IsBurst(slink->value.cptr) ? "*" : IsBurstAck(slink->value.cptr) ? "!" :
240 IsService(slink->value.cptr) ? "=" :IsHub(slink->value.cptr) ? "+" : " ",
241 cli_name(slink->value.cptr));
242 send_reply(sptr, RPL_DATASTR, outbuf);
245 send_reply(sptr, RPL_DATASTR, "<none>");
247 /* Send 'END OF CHECK' message */
248 send_reply(sptr, RPL_ENDOFCHECK, " ");
251 void checkClient(struct Client *sptr, struct Client *acptr, int flags) {
252 struct Channel *chptr;
253 struct Membership *lp;
254 char outbuf[BUFSIZE], *ptr;
258 send_reply(sptr, RPL_CHKHEAD, "user", acptr->cli_name);
259 send_reply(sptr, RPL_DATASTR, " ");
261 ircd_snprintf(0, outbuf, sizeof(outbuf), " Nick: %s (%s%s)", acptr->cli_name, NumNick(acptr));
262 send_reply(sptr, RPL_DATASTR, outbuf);
264 ircd_snprintf(0, outbuf, sizeof(outbuf), " Signed on: %s", MyUser(acptr)?myctime(acptr->cli_firsttime):"<unknown>");
265 send_reply(sptr, RPL_DATASTR, outbuf);
267 ircd_snprintf(0, outbuf, sizeof(outbuf), " Timestamp: %s (%d)", myctime(acptr->cli_lastnick), acptr->cli_lastnick);
268 send_reply(sptr, RPL_DATASTR, outbuf);
270 ircd_snprintf(0, outbuf, sizeof(outbuf), " Ident: %s", acptr->cli_user->username);
271 send_reply(sptr, RPL_DATASTR, outbuf);
273 ircd_snprintf(0, outbuf, sizeof(outbuf), "Current Hostmask: %s", acptr->cli_user->host);
274 send_reply(sptr, RPL_DATASTR, outbuf);
276 ircd_snprintf(0, outbuf, sizeof(outbuf), " Real Host: %s (%s)", acptr->cli_user->realhost, ircd_ntoa(&(cli_ip(acptr))));
277 send_reply(sptr, RPL_DATASTR, outbuf);
279 if(IsAccount(acptr)) {
280 ircd_snprintf(0, outbuf, sizeof(outbuf), " Account: %s (%s)", acptr->cli_user->account, acptr->cli_user->acc_create?myctime(acptr->cli_user->acc_create):"0");
281 send_reply(sptr, RPL_DATASTR, outbuf);
284 if(IsFakeHost(acptr)) {
285 ircd_snprintf(0, outbuf, sizeof(outbuf), " Fake Host: %s%c", acptr->cli_user->fakehost, COLOR_OFF);
286 send_reply(sptr, RPL_DATASTR, outbuf);
289 ircd_snprintf(0, outbuf, sizeof(outbuf), " Real Name: %s%c", cli_info(acptr), COLOR_OFF);
290 send_reply(sptr, RPL_DATASTR, outbuf);
293 send_reply(sptr, RPL_DATASTR, " Status: IRC Operator");
295 ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected to: %s", cli_name(acptr->cli_user->server));
296 send_reply(sptr, RPL_DATASTR, outbuf);
298 ptr = umode_str(acptr);
300 strcpy(outbuf, " Umode(s): <none>");
302 ircd_snprintf(0, outbuf, sizeof(outbuf), " Umode(s): +%s", ptr);
303 send_reply(sptr, RPL_DATASTR, outbuf);
305 if(acptr->cli_user->joined == 0)
306 send_reply(sptr, RPL_DATASTR, " Channel(s): <none>");
307 else if(!(flags & CHECK_CHECKCHAN) && acptr->cli_user->joined > 50) {
308 /* NB. As a sanity check, we DO NOT show the individual channels the
309 * client is on if it is on > 50 channels. This is to prevent the ircd
310 * barfing ala Uworld when someone does /quote check Q :).. (I shouldn't imagine
311 * an Oper would want to see every single channel 'x' client is on anyway if
312 * they are on *that* many).
314 ircd_snprintf(0, outbuf, sizeof(outbuf), " Channel(s): - (total: %u)", acptr->cli_user->joined);
315 send_reply(sptr, RPL_DATASTR, outbuf);
318 char chntext[BUFSIZE];
319 int len = strlen(" Channel(s): ");
320 int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name);
323 strcpy(chntext, " Channel(s): ");
324 for(lp = acptr->cli_user->channel; lp; lp = lp->next_channel) {
326 if(len + strlen(chptr->chname) + mlen > BUFSIZE - 5) {
327 send_reply(sptr, RPL_DATASTR, chntext);
329 strcpy(chntext, " Channel(s): ");
330 len = strlen(chntext);
333 *(chntext + len++) = '-';
334 if(is_chan_op(acptr, chptr))
335 *(chntext + len++) = '@';
336 else if(has_voice(acptr, chptr))
337 *(chntext + len++) = '+';
338 else if(IsZombie(lp))
339 *(chntext + len++) = '!';
341 *(chntext + len) = '\0';
343 strcpy(chntext + len, chptr->chname);
344 len += strlen(chptr->chname);
345 strcat(chntext + len, " ");
349 if(chntext[0] != '\0')
350 send_reply(sptr, RPL_DATASTR, chntext);
353 /* If client processing command ISN'T target (or a registered
354 * Network Service), show idle time since the last time we
358 nowr = CurrentTime - acptr->cli_user->last;
359 ircd_snprintf(0, outbuf, sizeof(outbuf), " Idle for: %d days, %02ld:%02ld:%02ld",
360 nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
361 send_reply(sptr, RPL_DATASTR, outbuf);
364 /* Away message (if applicable) */
365 if(acptr->cli_user->away) {
366 ircd_snprintf(0, outbuf, sizeof(outbuf), " Away message: %s", acptr->cli_user->away);
367 send_reply(sptr, RPL_DATASTR, outbuf);
370 /* If local user.. */
372 send_reply(sptr, RPL_DATASTR, " ");
373 ircd_snprintf(0, outbuf, sizeof(outbuf), " Port: %d", cli_listener(acptr)->addr.port);
374 send_reply(sptr, RPL_DATASTR, outbuf);
375 ircd_snprintf(0, outbuf, sizeof(outbuf), " Data sent: %0.3u bytes", cli_receiveB(acptr));
376 send_reply(sptr, RPL_DATASTR, outbuf);
377 ircd_snprintf(0, outbuf, sizeof(outbuf), " Data received: %0.3u bytes", cli_sendB(acptr));
378 send_reply(sptr, RPL_DATASTR, outbuf);
379 ircd_snprintf(0, outbuf, sizeof(outbuf), " receiveQ size: %d bytes (max. %d bytes)", DBufLength(&(cli_recvQ(acptr))), feature_int(FEAT_CLIENT_FLOOD));
380 send_reply(sptr, RPL_DATASTR, outbuf);
381 ircd_snprintf(0, outbuf, sizeof(outbuf), " sendQ size: %d bytes (max. %d bytes)", DBufLength(&(cli_sendQ(acptr))), get_sendq(acptr));
382 send_reply(sptr, RPL_DATASTR, outbuf);
385 send_reply(sptr, RPL_ENDOFCHECK, " ");
388 void checkChannel(struct Client *sptr, struct Channel *chptr) {
389 char outbuf[TOPICLEN + MODEBUFLEN + 64], modebuf[MODEBUFLEN], parabuf[MODEBUFLEN];
392 send_reply(sptr, RPL_CHKHEAD, "channel", chptr->chname);
393 send_reply(sptr, RPL_DATASTR, " ");
396 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Creation time: %s", myctime(chptr->creationtime));
397 send_reply(sptr, RPL_DATASTR, outbuf);
400 if(strlen(chptr->topic) <= 0)
401 send_reply(sptr, RPL_DATASTR, " Topic: <none>");
403 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Topic: %s", chptr->topic);
404 send_reply(sptr, RPL_DATASTR, outbuf);
405 ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set by: %s", chptr->topic_nick);
406 send_reply(sptr, RPL_DATASTR, outbuf);
409 strcpy(outbuf, "Channel mode(s): ");
413 channel_modes(sptr, modebuf, parabuf, sizeof(modebuf), chptr, 0);
415 if(modebuf[1] == '\0')
416 strcat(outbuf, "<none>");
418 strcat(outbuf, modebuf);
420 strcat(outbuf, parabuf);
423 strcat(outbuf, modebuf);
425 send_reply(sptr, RPL_DATASTR, outbuf);
426 /* Don't send 'END OF CHECK' message, it's sent in checkUsers, which is called after this. */
429 void checkUsers(struct Client *sptr, struct Channel *chptr, int flags) {
430 struct Membership *lp;
431 struct Ban *channelban;
432 struct Client *acptr;
434 char outbuf[BUFSIZE], ustat[64];
435 int cntr = 0, opcntr = 0, vcntr = 0, clones = 0, bans = 0, c = 0, authed = 0;
437 if(flags & CHECK_SHOWUSERS) send_reply(sptr, RPL_DATASTR, "Users (@ = op, + = voice)");
439 for(lp = chptr->members; lp; lp = lp->next_member) {
443 if((c = checkClones(chptr, acptr->cli_name, acptr->cli_user->realhost)) != 0) {
444 ircd_snprintf(0, ustat, sizeof(ustat), "%2d ", c);
450 if(chptr && is_chan_op(acptr, chptr)) {
455 else if(chptr && has_voice(acptr, chptr)) {
462 if((c = IsAccount(acptr)) != 0) ++authed;
463 if((flags & CHECK_SHOWUSERS) && (!(flags & CHECK_OPSONLY) || opped)) {
464 ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%c", acptr->cli_info, COLOR_OFF);
465 send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, acptr->cli_user->username,
466 (flags & CHECK_SHOWIPS) ? ircd_ntoa(&cli_ip(acptr)) : acptr->cli_user->realhost, outbuf,
467 (c ? acptr->cli_user->account : ""));
473 ircd_snprintf(0, outbuf, sizeof(outbuf), "Total users: %d (%d ops, %d voiced, %d clones, %d authed)",
474 cntr, opcntr, vcntr, clones, authed);
475 send_reply(sptr, RPL_DATASTR, outbuf);
477 /* Do not display bans if ! flags & CHECK_SHOWUSERS */
478 if(!(flags & CHECK_SHOWUSERS)) {
479 send_reply(sptr, RPL_ENDOFCHECK, " ");
483 send_reply(sptr, RPL_DATASTR, " ");
485 send_reply(sptr, RPL_DATASTR, "Bans/Exceptions on channel:");
487 for(channelban = chptr->banlist; channelban; channelban = channelban->next) {
488 ircd_snprintf(0, outbuf, sizeof(outbuf), "%c [%d] - %s - Set by %s, on %s", (channelban->flags & BAN_EXCEPTION)?'e':'b',
489 ++bans, channelban->banstr, channelban->who, myctime(channelban->when));
490 send_reply(sptr, RPL_DATASTR, outbuf);
494 send_reply(sptr, RPL_DATASTR, "<none>");
496 send_reply(sptr, RPL_ENDOFCHECK, " ");