/* * IRC - Internet Relay Chat, ircd/m_check.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * See file AUTHORS in IRC package for additional names of * the programmers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "channel.h" #include "check.h" #include "class.h" #include "client.h" #include "hash.h" #include "ircd.h" #include "ircd_alloc.h" #include "ircd_defs.h" #include "ircd_features.h" #include "ircd_reply.h" #include "ircd_string.h" #include "ircd_snprintf.h" #include "list.h" #include "listener.h" #include "match.h" #include "numeric.h" #include "numnicks.h" #include "querycmds.h" #include "send.h" #include "s_debug.h" #include "s_misc.h" #include "s_user.h" #include "support.h" #include #include #define CHECK_CHECKCHAN 0x01 /* -c */ #define CHECK_SHOWUSERS 0x02 /* ! -u */ #define CHECK_OPSONLY 0x04 /* -o */ #define CHECK_SHOWIPS 0x08 /* -i */ #define CHECK_CIDRMASK 0x10 /* automatically detected when performing a hostmask /CHECK */ /* * - ASUKA --------------------------------------------------------------------- * This is the implimentation of the CHECK function for Asuka. * Some of this code is from previous QuakeNet ircds, but most of it is mine.. * The old code was written by Durzel (durzel@quakenet.org). * * qoreQ (qoreQ@quakenet.org) - 08/14/2002 * ----------------------------------------------------------------------------- */ /* * Syntax: CHECK [-flags] * * Where valid flags are: * -c: Show channels when checking a hostmask. * -i: Show IPs instead of hostnames when displaying results. * -o: Only show channel operators when checking a channel. * -u: Hide users when checking a channel. * * can be of the form host, user@host, nick!user@host, * with host being host.domain.cc, 127.0.0.1 or 127.0.0.0/24. * Wildcards are supported. */ int mo_check(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Channel *chptr; struct Client *acptr; int flags = CHECK_SHOWUSERS, i; if (!HasPriv(sptr, PRIV_CHECK)) return send_reply(sptr, ERR_DISABLED, "CHECK"); if (parc < 2) { send_reply(sptr, ERR_NEEDMOREPARAMS, "CHECK"); return 0; } /* This checks to see if any flags have been supplied */ if ((parc >= 3) && (parv[2][0] == '-')) { for (i = 0; parv[2][i]; i++) { switch (parv[2][i]) { case 'c': flags |= CHECK_CHECKCHAN; break; case 'o': flags |= CHECK_OPSONLY; /* fall through */ case 'u': flags &= ~(CHECK_SHOWUSERS); break; case 'i': flags |= CHECK_SHOWIPS; break; default: /* might want to raise some sort of error here? */ break; } } } if (IsChannelName(parv[1])) /* channel */ { if ((chptr = FindChannel(parv[1]))) { checkChannel(sptr, chptr); checkUsers(sptr, chptr, flags); } else { send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]); } } else if ((acptr = FindClient(parv[1])) && !(FindServer(parv[1]))) /* client and not a server */ { if (!IsRegistered(acptr)) { send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]); return 0; } checkClient(sptr, acptr); } else if ((acptr = FindServer(parv[1]))) { /* server */ checkServer(sptr, acptr); } else if (checkHostmask(sptr, parv[1], flags) > 0) /* hostmask */ return 1; else /* no match */ send_reply(sptr, ERR_SEARCHNOMATCH, "CHECK", parv[1]); return 1; } static int checkClones(struct Channel *chptr, char *nick, char *host) { int clones = 0; struct Membership *lp; struct Client *acptr; for (lp = chptr->members; lp; lp = lp->next_member) { acptr = lp->user; if (!strcmp(acptr->cli_user->realhost, host) && strcmp(acptr->cli_name, nick)) { /* this is a clone */ clones++; } } return ((clones) ? clones + 1 : 0); } void checkUsers(struct Client *sptr, struct Channel *chptr, int flags) { struct Membership *lp; struct SLink *slp; struct Client *acptr; char outbuf[BUFSIZE], ustat[64]; int cntr = 0, opcntr = 0, hopcntr = 0, vcntr = 0, clones = 0, bans = 0, excepts = 0, c = 0, authed = 0; if (flags & CHECK_SHOWUSERS) { if (feature_bool(FEAT_HALFOPS)) send_reply(sptr, RPL_DATASTR, "Users (@ = op, % = halfop, + = voice, * = clone)"); else send_reply(sptr, RPL_DATASTR, "Users (@ = op, + = voice, * = clone)"); } for (lp = chptr->members; lp; lp = lp->next_member) { int opped = 0; acptr = lp->user; if ((c = checkClones(chptr, acptr->cli_name, acptr->cli_user->realhost)) != 0) { ircd_snprintf(0, ustat, sizeof(ustat), "%2d ", c); clones++; } else { strcpy(ustat, " "); } if (chptr && IsChanOp(lp)) { strcat(ustat, "@"); opcntr++; opped = 1; } else if (chptr && IsHalfOp(lp)) { strcat(ustat, "%"); hopcntr++; } else if (chptr && HasVoice(lp)) { strcat(ustat, "+"); vcntr++; } else strcat(ustat, " "); if ((c = IsAccount(acptr)) != 0) ++authed; if ((flags & CHECK_SHOWUSERS) || ((flags & CHECK_OPSONLY) && opped)) { ircd_snprintf(0, outbuf, sizeof(outbuf), "%s%c", acptr->cli_info, COLOR_OFF); send_reply(sptr, RPL_CHANUSER, ustat, acptr->cli_name, acptr->cli_user->realusername, (flags & CHECK_SHOWIPS) ? ircd_ntoa((const char*)&(cli_ip(acptr))) : acptr->cli_user->realhost, outbuf, (c ? acptr->cli_user->account : "")); } cntr++; } send_reply(sptr, RPL_DATASTR, " "); if (feature_bool(FEAT_HALFOPS)) ircd_snprintf(0, outbuf, sizeof(outbuf), "Total users:: %d (%d ops, %d half ops, %d voiced, %d clones, %d authed)", cntr, opcntr, hopcntr, vcntr, clones, authed); else ircd_snprintf(0, outbuf, sizeof(outbuf), "Total users:: %d (%d ops, %d voiced, %d clones, %d authed)", cntr, opcntr, vcntr, clones, authed); send_reply(sptr, RPL_DATASTR, outbuf); send_reply(sptr, RPL_DATASTR, " "); /* Do not display bans if ! flags & CHECK_SHOWUSERS */ if (!(flags & CHECK_SHOWUSERS)) { send_reply(sptr, RPL_ENDOFCHECK, " "); return; } /* Bans */ send_reply(sptr, RPL_DATASTR, "Bans on channel::"); for (slp = chptr->banlist; slp; slp = slp->next) { ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s - Set by %s, on %s", ++bans, slp->value.ban.banstr, slp->value.ban.who, myctime(slp->value.ban.when)); send_reply(sptr, RPL_DATASTR, outbuf); } if (bans == 0) send_reply(sptr, RPL_DATASTR, ""); /* Excepts */ if (feature_bool(FEAT_EXCEPTS)) { send_reply(sptr, RPL_DATASTR, "Excepts on channel::"); for (slp = chptr->exceptlist; slp; slp = slp->next) { ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s - Set by %s, on %s", ++excepts, slp->value.except.exceptstr, slp->value.except.who, myctime(slp->value.except.when)); send_reply(sptr, RPL_DATASTR, outbuf); } if (excepts == 0) send_reply(sptr, RPL_DATASTR, ""); } send_reply(sptr, RPL_ENDOFCHECK, " "); } void checkChannel(struct Client *sptr, struct Channel *chptr) { char outbuf[TOPICLEN + MODEBUFLEN + 64], modebuf[MODEBUFLEN], parabuf[MODEBUFLEN]; /* Header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "channel", chptr->chname); send_reply(sptr, RPL_DATASTR, " "); /* Creation Time */ ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Creation time:: %s", myctime(chptr->creationtime)); send_reply(sptr, RPL_DATASTR, outbuf); /* Topic */ if (strlen(chptr->topic) <= 0) send_reply(sptr, RPL_DATASTR, " Topic:: "); else { ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Topic:: %s", chptr->topic); send_reply(sptr, RPL_DATASTR, outbuf); /* ..set by */ ircd_snprintf(sptr, outbuf, sizeof(outbuf), " Set by:: %s", chptr->topic_nick); send_reply(sptr, RPL_DATASTR, outbuf); } /* Channel Modes */ strcpy(outbuf, "Channel mode(s):: "); modebuf[0] = '\0'; parabuf[0] = '\0'; channel_modes(sptr, modebuf, parabuf, sizeof(modebuf), chptr); if(modebuf[1] == '\0') strcat(outbuf, ""); else if(*parabuf) { strcat(outbuf, modebuf); strcat(outbuf, " "); strcat(outbuf, parabuf); } else strcat(outbuf, modebuf); send_reply(sptr, RPL_DATASTR, outbuf); /* Don't send 'END OF CHECK' message, it's sent in checkUsers, which is called after this. */ } void checkClient(struct Client *sptr, struct Client *acptr) { struct Channel *chptr; struct Membership *lp; char outbuf[BUFSIZE]; time_t nowr; /* Header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "user", acptr->cli_name); send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), " Nick:: %s (%s%s)", acptr->cli_name, NumNick(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); if (MyUser(acptr)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Signed on:: %s", myctime(acptr->cli_firsttime)); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), " Timestamp:: %s (%d)", myctime(acptr->cli_lastnick), acptr->cli_lastnick); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " User/Hostmask:: %s@%s (%s)", acptr->cli_user->username, acptr->cli_user->host, ircd_ntoa((const char*) &(cli_ip(acptr)))); send_reply(sptr, RPL_DATASTR, outbuf); if (((feature_int(FEAT_HOST_HIDING_STYLE) == 1) ? HasHiddenHost(acptr) : IsHiddenHost(acptr)) || IsSetHost(acptr)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Real User/Host:: %s@%s", acptr->cli_user->realusername, acptr->cli_user->realhost); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), " Real Name:: %s%c", cli_info(acptr), COLOR_OFF); send_reply(sptr, RPL_DATASTR, outbuf); if (IsService(acptr) == -1) send_reply(sptr, RPL_DATASTR, " Status:: Network Service"); else if (IsAnOper(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator"); ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected to:: %s", cli_name(acptr->cli_user->server)); send_reply(sptr, RPL_DATASTR, outbuf); /* +s (SERV_NOTICE) is not relayed to us from remote servers, * so we cannot tell if a remote client has that mode set. * And hacking it onto the end of the output of umode_str is EVIL BAD AND WRONG * (and breaks if the user is +r) so we won't do that either. */ if (strlen(umode_str(acptr)) < 1) strcpy(outbuf, " Umode(s):: "); else ircd_snprintf(0, outbuf, sizeof(outbuf), " Umode(s):: +%s", umode_str(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); if (acptr->cli_user->joined == 0) send_reply(sptr, RPL_DATASTR, " Channel(s):: "); else if (acptr->cli_user->joined > 50) { /* NB. As a sanity check, we DO NOT show the individual channels the * client is on if it is on > 50 channels. This is to prevent the ircd * barfing ala Uworld when someone does /quote check Q :).. (I shouldn't imagine * an Oper would want to see every single channel 'x' client is on anyway if * they are on *that* many). */ ircd_snprintf(0, outbuf, sizeof(outbuf), " Channel(s):: - (total: %u)", acptr->cli_user->joined); send_reply(sptr, RPL_DATASTR, outbuf); } else { char chntext[BUFSIZE]; int len = strlen(" Channel(s):: "); int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name); *chntext = '\0'; strcpy(chntext, " Channel(s):: "); for (lp = acptr->cli_user->channel; lp; lp = lp->next_channel) { chptr = lp->channel; if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, RPL_DATASTR, chntext); *chntext = '\0'; strcpy(chntext, " Channel(s):: "); len = strlen(chntext); } if (IsDeaf(acptr)) *(chntext + len++) = '-'; if (is_chan_op(acptr, chptr)) *(chntext + len++) = '@'; if (is_half_op(acptr, chptr)) *(chntext + len++) = '%'; if (IsOper(sptr) && !ShowChannel(sptr,chptr)) *(chntext + len++) = '*'; if (IsZombie(lp)) *(chntext + len++) = '!'; if (len) *(chntext + len) = '\0'; strcpy(chntext + len, chptr->chname); len += strlen(chptr->chname); strcat(chntext + len, " "); len++; } if (chntext[0] != '\0') send_reply(sptr, RPL_DATASTR, chntext); } /* If client processing command ISN'T target (or a registered * Network Service), show idle time since the last time we * parsed something. */ if (MyUser(acptr) && !(IsService(acptr) == -1) && !(strCasediff(acptr->cli_name, sptr->cli_name) == 0)) { nowr = CurrentTime - acptr->cli_user->last; ircd_snprintf(0, outbuf, sizeof(outbuf), " Idle for:: %d days, %02ld:%02ld:%02ld", nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60); send_reply(sptr, RPL_DATASTR, outbuf); } /* Away message (if applicable) */ if (acptr->cli_user->away) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Away message:: %s", acptr->cli_user->away); send_reply(sptr, RPL_DATASTR, outbuf); } /* If local user.. */ if (MyUser(acptr)) { send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), " Ports:: %d -> %d (client -> server)", cli_port(acptr), cli_listener(acptr)->port); send_reply(sptr, RPL_DATASTR, outbuf); if (feature_bool(FEAT_CHECK_EXTENDED)) { /* Note: sendq = receiveq for a client (it makes sense really) */ ircd_snprintf(0, outbuf, sizeof(outbuf), " Data sent:: %u.%0.3u Kb (%u protocol messages)", cli_receiveK(acptr), cli_receiveB(acptr), cli_receiveM(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Data received:: %u.%0.3u Kb (%u protocol messages)", cli_sendK(acptr), cli_sendB(acptr), cli_sendM(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " receiveQ size:: %d bytes (max. %d bytes)", DBufLength(&(cli_recvQ(acptr))), feature_int(FEAT_CLIENT_FLOOD)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " sendQ size:: %d bytes (max. %d bytes)", DBufLength(&(cli_sendQ(acptr))), get_sendq(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } } /* Send 'END OF CHECK' message */ send_reply(sptr, RPL_ENDOFCHECK, " "); } void checkServer(struct Client *sptr, struct Client *acptr) { char outbuf[BUFSIZE]; /* Header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "server", acptr->cli_name); send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected at:: %s", myctime(acptr->cli_serv->timestamp)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Server name:: %s", acptr->cli_name); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Numeric:: %s --> %d", NumServ(acptr), base64toint(acptr->cli_yxx)); send_reply(sptr, RPL_DATASTR, outbuf); ircd_snprintf(0, outbuf, sizeof(outbuf), " Users:: %d / %d", cli_serv(acptr)->clients, base64toint(cli_serv(acptr)->nn_capacity)); send_reply(sptr, RPL_DATASTR, outbuf); if (IsBurst(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Bursting"); else if (IsBurstAck(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Awaiting EOB Ack"); else if (IsService(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Network Service"); else if (IsHub(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: Network Hub"); if (feature_bool(FEAT_CHECK_EXTENDED)) { int dlinkc = 0; struct DLink* slink = NULL; send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_DATASTR, "Downlinks::"); for (slink = cli_serv(acptr)->down; slink; slink = slink->next) { ircd_snprintf(0, outbuf, sizeof(outbuf), "[%d] - %s%s", ++dlinkc, IsBurst(slink->value.cptr) ? "*" : IsBurstAck(slink->value.cptr) ? "!" : IsService(slink->value.cptr) ? "=" : IsHub(slink->value.cptr) ? "+" : " ", cli_name(slink->value.cptr)); send_reply(sptr, RPL_DATASTR, outbuf); } if (!dlinkc) send_reply(sptr, RPL_DATASTR, ""); } /* Send 'END OF CHECK' message */ send_reply(sptr, RPL_ENDOFCHECK, " "); } signed int checkHostmask(struct Client *sptr, char *hoststr, int flags) { struct Client *acptr; struct Channel *chptr; struct Membership *lp; int count = 0, found = 0, cidr_check_bits = 0; char outbuf[BUFSIZE]; char targhost[NICKLEN + USERLEN + HOSTLEN + 3], curhost[NICKLEN + USERLEN + HOSTLEN + 3]; char nickm[NICKLEN + 1], userm[USERLEN + 1], hostm[HOSTLEN + 1]; char *p = NULL; struct in_addr cidr_check; strcpy(nickm,"*"); strcpy(userm,"*"); strcpy(hostm,"*"); if (!strchr(hoststr, '!') && !strchr(hoststr, '@')) ircd_strncpy(hostm,hoststr,HOSTLEN); else { if ((p = strchr(hoststr, '@'))) { *p++ = '\0'; if (*p) ircd_strncpy(hostm,p, HOSTLEN); } /* Get the nick!user mask */ if ((p = strchr(hoststr, '!'))) { *p++ = '\0'; if (*p) ircd_strncpy(userm,p,USERLEN); if (*hoststr) ircd_strncpy(nickm,hoststr,NICKLEN); } else if (*hoststr) { /* Durz: We should only do the following *IF* the hoststr has not already been * copied into hostm (ie. neither ! or @ specified).. otherwise, when we do * /quote check *.barrysworld.com - we end up with targhost as: *!*.barryswo@*.barrysworld.com */ ircd_strncpy(userm,hoststr,USERLEN); } } if ((p = strchr(hostm, '/')) || inet_aton(hostm, &cidr_check)) { if (p) *p = '\0'; if (inet_aton(hostm, &cidr_check)) { cidr_check_bits = p ? atoi(p + 1) : 32; if ((cidr_check_bits >= 0) && (cidr_check_bits <= 32)) { flags |= CHECK_CIDRMASK; cidr_check.s_addr &= NETMASK(cidr_check_bits); } } if (p) *p = '/'; } /* Copy formatted string into "targhost" buffer */ ircd_snprintf(0, targhost, sizeof(targhost), "%s!%s@%s", nickm, userm, hostm); targhost[sizeof(targhost) - 1] = '\0'; /* Note: we have to exclude the last client struct as it is not a real client * structure, and therefore any attempt to access elements in it would cause * a segfault. */ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { /* Dont process if acptr is a unregistered client, a server or a ping */ if (!IsRegistered(acptr) || IsServer(acptr)) continue; if (IsMe(acptr)) /* Always the last acptr record */ break; if(count > feature_int(FEAT_MAX_CHECK_OUTPUT)) { /* sanity stuff */ send_reply(sptr, RPL_ENDOFCHECK, " "); break; } /* Copy host info into buffer */ curhost[0] = '\0'; ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", acptr->cli_name, acptr->cli_user->realusername, acptr->cli_user->realhost); if (flags & CHECK_CIDRMASK) { if (((cli_ip(acptr).s_addr & NETMASK(cidr_check_bits)) == cidr_check.s_addr) && !match(nickm, acptr->cli_name) && (!match(userm, acptr->cli_user->realusername) || !match(userm, acptr->cli_user->username))) found = 1; } else { if(match((const char*)targhost,(const char*)curhost) == 0) found = 1; else { curhost[0] = '\0'; ircd_snprintf(0, curhost, sizeof(curhost), "%s!%s@%s", acptr->cli_name, acptr->cli_user->username, acptr->cli_user->host); if(match((const char*)targhost,(const char*)curhost) == 0) found = 1; } } if (found == 1) { found = 0; /* reset that so it doesn't get crazy go nuts */ /* Show header if we've found at least 1 record */ if (count == 0) { /* Output header */ send_reply(sptr, RPL_DATASTR, " "); send_reply(sptr, RPL_CHKHEAD, "host", targhost); send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), "%s %-*s%-*s%s", "No.", (NICKLEN + 2 ), "Nick", (USERLEN + 2), "User", "Host"); send_reply(sptr, RPL_DATASTR, outbuf); } ircd_snprintf(0, outbuf, sizeof(outbuf), "%-4d %-*s%-*s%s", (count+1), (NICKLEN + 2), acptr->cli_name, (USERLEN + 2), acptr->cli_user->realusername, (flags & CHECK_SHOWIPS) ? ircd_ntoa((const char*)&(cli_ip(acptr))) : acptr->cli_user->realhost); send_reply(sptr, RPL_DATASTR, outbuf); /* Show channel output (if applicable) - the 50 channel limit sanity check * is specifically to prevent coredumping when someone lamely tries to /check * Q or some other channel service... */ if (flags & CHECK_CHECKCHAN) { if (acptr->cli_user->joined > 0 && acptr->cli_user->joined <= 50) { char chntext[BUFSIZE]; int len = strlen(" on channels: "); int mlen = strlen(me.cli_name) + len + strlen(sptr->cli_name); *chntext = '\0'; strcpy(chntext, " on channels: "); for (lp = acptr->cli_user->channel; lp; lp = lp->next_channel) { chptr = lp->channel; if (len + strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, RPL_DATASTR, chntext); *chntext = '\0'; strcpy(chntext, " on channels: "); len = strlen(chntext); } if (IsDeaf(acptr)) *(chntext + len++) = '-'; if (is_chan_op(acptr, chptr)) *(chntext + len++) = '@'; if (is_half_op(acptr, chptr)) *(chntext + len++) = '%'; if (IsOper(sptr) && !ShowChannel(sptr,chptr)) *(chntext + len++) = '*'; else if (has_voice(acptr, chptr)) *(chntext + len++) = '+'; else if (IsZombie(lp)) *(chntext + len++) = '!'; if (len) *(chntext + len) = '\0'; strcpy(chntext + len, chptr->chname); len += strlen(chptr->chname); strcat(chntext + len, " "); len++; } if (chntext[0] != '\0') send_reply(sptr, RPL_DATASTR, chntext); send_reply(sptr, RPL_DATASTR, " "); } } count++; } } if (count > 0) { send_reply(sptr, RPL_DATASTR, " "); ircd_snprintf(0, outbuf, sizeof(outbuf), "Matching records found:: %d", count); send_reply(sptr, RPL_DATASTR, outbuf); send_reply(sptr, RPL_ENDOFCHECK, " "); } return count; }