/** Check whether a client matches a target mask. * @param[in] from Client trying to send a message (ignored). * @param[in] one Client being considered as a target. * @param[in] mask Mask for matching against. * @param[in] what Type of match (either MATCH_HOST or MATCH_SERVER). * @return Non-zero if \a one matches, zero if not. */ static int match_it(struct Client *from, struct Client *one, const char *mask, int what) { switch (what) { case MATCH_HOST: return (match(mask, cli_user(one)->host) == 0 || (HasHiddenHost(one) && match(mask, cli_user(one)->realhost) == 0)); case MATCH_SERVER: default: return (match(mask, cli_name(cli_user(one)->server)) == 0); } }
/** Check whether a client matches a target mask. * @param[in] from Client trying to send a message (ignored). * @param[in] one Client being considered as a target. * @param[in] mask Mask for matching against. * @param[in] addr IP address prefix to match against. * @param[in] nbits Number of bits in \a addr (> 128 if none valid). * @param[in] what Type of match (either MATCH_HOST or MATCH_SERVER). * @return Non-zero if \a one matches, zero if not. */ static int match_it(struct Client *from, struct Client *one, const char *mask, struct irc_in_addr *addr, unsigned char nbits, int what) { switch (what) { case MATCH_HOST: return ((nbits <= 128 && ipmask_check(&cli_ip(one), addr, nbits)) || match(mask, cli_user(one)->host) == 0 || (HasHiddenHost(one) && match(mask, cli_user(one)->realhost) == 0)); case MATCH_SERVER: default: return (match(mask, cli_name(cli_user(one)->server)) == 0); } }
static void userhost_formatter(struct Client* cptr, struct Client *sptr, struct MsgBuf* mb) { assert(IsUser(cptr)); msgq_append(0, mb, "%s%s=%c%s@%s", cli_name(cptr), SeeOper(sptr,cptr) ? "*" : "", cli_user(cptr)->away ? '-' : '+', cli_user(cptr)->username, /* Do not *EVER* change this to give opers the real host. * Too many scripts rely on this data and can inadvertently * publish the user's real host, thus breaking the security * of +x. If an oper wants the real host, he should go to * /whois to get it. */ HasHiddenHost(cptr) && (sptr != cptr) ? cli_user(cptr)->host : cli_user(cptr)->realhost); }
/* * ms_account - server message handler * * parv[0] = sender prefix * parv[1] = numeric of client to act on * parv[2] = account name (12 characters or less) */ int ms_account(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; int hidden; if (parc < 3) return need_more_params(sptr, "ACCOUNT"); if (!IsServer(sptr)) return protocol_violation(cptr, "ACCOUNT from non-server %s", cli_name(sptr)); if (!(acptr = findNUser(parv[1]))) return 0; /* Ignore ACCOUNT for a user that QUIT; probably crossed */ if (IsAccount(acptr)) return protocol_violation(cptr, "ACCOUNT for already registered user %s " "(%s -> %s)", cli_name(acptr), cli_user(acptr)->account, parv[2]); assert(0 == cli_user(acptr)->account[0]); if (strlen(parv[2]) > ACCOUNTLEN) { return protocol_violation(cptr, "Received account (%s) longer than %d for %s; ignoring.", parv[2], ACCOUNTLEN, cli_name(acptr)); } if (parc > 3) { cli_user(acptr)->acc_create = atoi(parv[3]); Debug((DEBUG_DEBUG, "Received timestamped account: account \"%s\", " "timestamp %Tu", parv[2], cli_user(acptr)->acc_create)); } hidden = HasHiddenHost(acptr); SetAccount(acptr); ircd_strncpy(cli_user(acptr)->account, parv[2], ACCOUNTLEN); if (!hidden) hide_hostmask(acptr); sendcmdto_serv_butone(sptr, CMD_ACCOUNT, cptr, cli_user(acptr)->acc_create ? "%C %s %Tu" : "%C %s", acptr, cli_user(acptr)->account, cli_user(acptr)->acc_create); return 0; }
void checkClient(struct Client *sptr, struct Client *acptr) { struct Channel *chptr; struct Membership *lp; char outbuf[BUFSIZE]; char *privs; 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(cli_user(acptr)->server)) { if (acptr) send_reply(sptr, RPL_DATASTR, " Status:: Network Service"); else if (IsAdmin(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: IRC Administrator (service)"); else if (IsAnOper(acptr)) send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator (service)"); else send_reply(sptr, RPL_DATASTR, " Status:: Client (service)"); } else if (IsAdmin(acptr)) { send_reply(sptr, RPL_DATASTR, " Status:: IRC Administrator"); } else if (IsAnOper(acptr)) { send_reply(sptr, RPL_DATASTR, " Status:: IRC Operator"); } else { send_reply(sptr, RPL_DATASTR, " Status:: Client"); } if (MyUser(acptr)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " Class:: %s", get_client_class(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } privs = client_print_privs(acptr); if (strlen(privs) > 1) client_check_privs(acptr, sptr); ircd_snprintf(0, outbuf, sizeof(outbuf), " Connected to:: %s", cli_name(acptr->cli_user->server)); send_reply(sptr, RPL_DATASTR, outbuf); if (cli_version(acptr)) { if (strlen(cli_version(acptr)) > 0) { ircd_snprintf(0, outbuf, sizeof(outbuf), " CTCP Version:: %s", cli_version(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } } if (cli_user(acptr) && !EmptyString(cli_user(acptr)->swhois)) { ircd_snprintf(0, outbuf, sizeof(outbuf), " SWHOIS:: %s", cli_user(acptr)->swhois); send_reply(sptr, RPL_DATASTR, outbuf); } if (cli_webirc(acptr)) { if (strlen(cli_webirc(acptr)) > 0) { ircd_snprintf(0, outbuf, sizeof(outbuf), " WebIRC:: %s", cli_webirc(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } } if (cli_sslclifp(acptr) && (strlen(cli_sslclifp(acptr)) > 0)) { ircd_snprintf(0, outbuf, sizeof(outbuf), "SSL Fingerprint:: %s", cli_sslclifp(acptr)); send_reply(sptr, RPL_DATASTR, outbuf); } if (MyUser(acptr)) get_eflags(sptr, acptr); /* +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):: <none>"); 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):: <none>"); 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, " "); }
/* * m_who - generic message handler * * parv[0] = sender prefix * parv[1] = nickname mask list * parv[2] = additional selection flag, only 'o' for now. * and %flags to specify what fields to output * plus a ,querytype if the t flag is specified * so the final thing will be like o%tnchu,777 * parv[3] = _optional_ parameter that overrides parv[1] * This can be used as "/quote who foo % :The Black Hacker * to find me, parv[3] _can_ contain spaces !. */ int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char *mask; /* The mask we are looking for */ char ch; /* Scratch char register */ struct Channel *chptr; /* Channel to show */ struct Client *acptr; /* Client to show */ int bitsel; /* Mask of selectors to apply */ int matchsel; /* Wich fields the match should apply on */ int counter; /* Query size counter, initially used to count fields */ int commas; /* Does our mask contain any comma ? If so is a list.. */ int fields; /* Mask of fields to show */ int isthere = 0; /* When this set the user is member of chptr */ char *nick; /* Single element extracted from the mask list */ char *p; /* Scratch char pointer */ char *qrt; /* Pointer to the query type */ static char mymask[512]; /* To save the mask before corrupting it */ unsigned int who_marker; /* Used to mark clients we've touched */ /* Let's find where is our mask, and if actually contains something */ mask = ((parc > 1) ? parv[1] : 0); if (parc > 3 && parv[3]) mask = parv[3]; if (mask && ((mask[0] == '\0') || (mask[1] == '\0' && ((mask[0] == '0') || (mask[0] == '*'))))) mask = 0; /* Evaluate the flags now, we consider the second parameter as "matchFlags%fieldsToInclude,querytype" */ bitsel = fields = counter = matchsel = 0; qrt = 0; if (parc > 2 && parv[2] && *parv[2]) { p = parv[2]; while (((ch = *(p++))) && (ch != '%') && (ch != ',')) switch (ch) { case 'o': case 'O': bitsel |= WHOSELECT_OPER; continue; case 'x': case 'X': if (HasPriv(sptr, PRIV_WHOX)) { log_write(LS_WHO, L_INFO, LOG_NOSNOTICE, "%#C WHO %s %s", sptr, (BadPtr(parv[3]) ? parv[1] : parv[3]), parv[2]); bitsel |= WHOSELECT_EXTRA; } continue; case 'n': case 'N': matchsel |= WHO_FIELD_NIC; continue; case 'u': case 'U': matchsel |= WHO_FIELD_UID; continue; case 'h': case 'H': matchsel |= WHO_FIELD_HOS; continue; case 'i': case 'I': matchsel |= WHO_FIELD_NIP; continue; case 's': case 'S': matchsel |= WHO_FIELD_SER; continue; case 'r': case 'R': matchsel |= WHO_FIELD_REN; continue; case 'a': case 'A': matchsel |= WHO_FIELD_ACC; continue; } if (ch == '%') while ((ch = *p++) && (ch != ',')) { counter++; switch (ch) { case 'c': case 'C': fields |= WHO_FIELD_CHA; break; case 'd': case 'D': fields |= WHO_FIELD_DIS; break; case 'f': case 'F': fields |= WHO_FIELD_FLA; break; case 'h': case 'H': fields |= WHO_FIELD_HOS; break; case 'i': case 'I': fields |= WHO_FIELD_NIP; break; case 'l': case 'L': fields |= WHO_FIELD_IDL; case 'n': case 'N': fields |= WHO_FIELD_NIC; break; case 'r': case 'R': fields |= WHO_FIELD_REN; break; case 's': case 'S': fields |= WHO_FIELD_SER; break; case 't': case 'T': fields |= WHO_FIELD_QTY; break; case 'u': case 'U': fields |= WHO_FIELD_UID; break; case 'a': case 'A': fields |= WHO_FIELD_ACC; break; default: break; } }; if (ch) qrt = p; } if (!matchsel) matchsel = WHO_FIELD_DEF; if (!fields) counter = 7; if (feature_bool(FEAT_HIS_WHO_SERVERNAME) && !IsAnOper(sptr)) matchsel &= ~WHO_FIELD_SER; if (qrt && (fields & WHO_FIELD_QTY)) { p = qrt; if (!((*p > '9') || (*p < '0'))) p++; if (!((*p > '9') || (*p < '0'))) p++; if (!((*p > '9') || (*p < '0'))) p++; *p = '\0'; } else qrt = 0; /* I'd love to add also a check on the number of matches fields per time */ counter = (2048 / (counter + 4)); if (mask && (strlen(mask) > 510)) mask[510] = '\0'; who_marker = get_client_marker(); commas = (mask && strchr(mask, ',')); /* First treat mask as a list of plain nicks/channels */ if (mask) { strcpy(mymask, mask); for (p = 0, nick = ircd_strtok(&p, mymask, ","); nick; nick = ircd_strtok(&p, 0, ",")) { if (IsChannelName(nick) && (chptr = FindChannel(nick))) { isthere = (find_channel_member(sptr, chptr) != 0); if (isthere || SEE_CHANNEL(sptr, chptr, bitsel)) { struct Membership* member; for (member = chptr->members; member; member = member->next_member) { acptr = member->user; if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr)) continue; if ((acptr != sptr) && (member->status & CHFL_ZOMBIE)) continue; if (!(isthere || (SEE_USER(sptr, acptr, bitsel)))) continue; if (!Process(acptr)) /* This can't be moved before other checks */ continue; if (!(isthere || (IsOper(sptr) && (bitsel & WHOSELECT_EXTRA) && HasPriv(sptr, PRIV_SEE_CHAN)) || (SHOW_MORE(sptr, counter)))) break; do_who(sptr, acptr, chptr, fields, qrt); } } } else { if ((acptr = FindUser(nick)) && ((!(bitsel & WHOSELECT_OPER)) || SeeOper(sptr,acptr)) && Process(acptr) && SHOW_MORE(sptr, counter)) { do_who(sptr, acptr, 0, fields, qrt); } } } } /* If we didn't have any comma in the mask treat it as a real mask and try to match all relevant fields */ if (!(commas || (counter < 1))) { int minlen, cset; static struct in_mask imask; if (mask) { matchcomp(mymask, &minlen, &cset, mask); if (matchcompIP(&imask, mask)) matchsel &= ~WHO_FIELD_NIP; if ((minlen > NICKLEN) || !(cset & NTL_IRCNK)) matchsel &= ~WHO_FIELD_NIC; if ((matchsel & WHO_FIELD_SER) && ((minlen > HOSTLEN) || (!(cset & NTL_IRCHN)) || (!markMatchexServer(mymask, minlen)))) matchsel &= ~WHO_FIELD_SER; if ((minlen > USERLEN) || !(cset & NTL_IRCUI)) matchsel &= ~WHO_FIELD_UID; if ((minlen > HOSTLEN) || !(cset & NTL_IRCHN)) matchsel &= ~WHO_FIELD_HOS; } /* First of all loop through the clients in common channels */ if ((!(counter < 1)) && matchsel) { struct Membership* member; struct Membership* chan; for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) { chptr = chan->channel; for (member = chptr->members; member; member = member->next_member) { acptr = member->user; if (!(IsUser(acptr) && Process(acptr))) continue; /* Now Process() is at the beginning, if we fail we'll never have to show this acptr in this query */ if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr)) continue; if ((mask) && ((!(matchsel & WHO_FIELD_NIC)) || matchexec(cli_name(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_UID)) || matchexec(cli_user(acptr)->username, mymask, minlen)) && ((!(matchsel & WHO_FIELD_SER)) || (!HasFlag(cli_user(acptr)->server, FLAG_MAP))) && ((!(matchsel & WHO_FIELD_HOS)) || matchexec(cli_user(acptr)->host, mymask, minlen)) && ((!(matchsel & WHO_FIELD_HOS)) || !HasSetHost(acptr) || !HasHiddenHost(acptr) || !IsAnOper(sptr) || matchexec(cli_user(acptr)->realhost, mymask, minlen)) && ((!(matchsel & WHO_FIELD_REN)) || matchexec(cli_info(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_NIP)) || ((HasHiddenHost(acptr) || HasSetHost(acptr)) && !IsAnOper(sptr)) || ((((cli_ip(acptr).s_addr & imask.mask.s_addr) != imask.bits.s_addr)) || (imask.fall && matchexec(ircd_ntoa((const char*) &(cli_ip(acptr))), mymask, minlen))))) continue; if (!SHOW_MORE(sptr, counter)) break; do_who(sptr, acptr, chptr, fields, qrt); } } } /* Loop through all clients :-\, if we still have something to match to and we can show more clients */ if ((!(counter < 1)) && matchsel) for (acptr = cli_prev(&me); acptr; acptr = cli_prev(acptr)) { if (!(IsUser(acptr) && Process(acptr))) continue; if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr)) continue; if (!(SEE_USER(sptr, acptr, bitsel))) continue; if ((mask) && ((!(matchsel & WHO_FIELD_NIC)) || matchexec(cli_name(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_UID)) || matchexec(cli_user(acptr)->username, mymask, minlen)) && ((!(matchsel & WHO_FIELD_SER)) || (!HasFlag(cli_user(acptr)->server, FLAG_MAP))) && ((!(matchsel & WHO_FIELD_HOS)) || matchexec(cli_user(acptr)->host, mymask, minlen)) && ((!(matchsel & WHO_FIELD_HOS)) || !HasSetHost(acptr) || !HasHiddenHost(acptr) || !IsAnOper(sptr) || matchexec(cli_user(acptr)->realhost, mymask, minlen)) && ((!(matchsel & WHO_FIELD_REN)) || matchexec(cli_info(acptr), mymask, minlen)) && ((!(matchsel & WHO_FIELD_NIP)) || (HasHiddenHost(acptr) && !IsAnOper(sptr)) || ((((cli_ip(acptr).s_addr & imask.mask.s_addr) != imask.bits.s_addr)) || (imask.fall && matchexec(ircd_ntoa((const char*) &(cli_ip(acptr))), mymask, minlen))))) continue; if (!SHOW_MORE(sptr, counter)) break; do_who(sptr, acptr, 0, fields, qrt); } } /* Make a clean mask suitable to be sent in the "end of" */ if (mask && (p = strchr(mask, ' '))) *p = '\0'; send_reply(sptr, RPL_ENDOFWHO, BadPtr(mask) ? "*" : mask); /* Notify the user if we decided that his query was too long */ if (counter < 0) send_reply(sptr, ERR_QUERYTOOLONG, "WHO"); return 0; }
/* * The function that actually prints out the WHO reply for a client found */ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, int fields, char* qrt) { char *p1; struct Channel *chptr = repchan; static char buf1[512]; /* NOTE: with current fields list and sizes this _cannot_ overrun, and also the message finally sent shouldn't ever be truncated */ p1 = buf1; buf1[1] = '\0'; /* If we don't have a channel and we need one... try to find it, unless the listing is for a channel service, we already know that there are no common channels, thus use PubChannel and not SeeChannel */ if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) && !IsChannelService(acptr)) { struct Membership* chan; for (chan = cli_user(acptr)->channel; chan && !chptr; chan = chan->next_channel) if (PubChannel(chan->channel) && (acptr == sptr || !IsZombie(chan))) chptr = chan->channel; } /* Place the fields one by one in the buffer and send it note that fields == NULL means "default query" */ if (fields & WHO_FIELD_QTY) /* Query type */ { *(p1++) = ' '; if (BadPtr(qrt)) *(p1++) = '0'; else while ((*qrt) && (*(p1++) = *(qrt++))); } if (!fields || (fields & WHO_FIELD_CHA)) { char *p2; *(p1++) = ' '; if ((p2 = (chptr ? chptr->chname : NULL))) while ((*p2) && (*(p1++) = *(p2++))); else *(p1++) = '*'; } if (!fields || (fields & WHO_FIELD_UID)) { char *p2 = cli_user(acptr)->username; *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (fields & WHO_FIELD_NIP) { const char* p2 = IsHiddenHost(acptr) && (!IsViewip(sptr) && acptr != sptr) ? feature_str(FEAT_HIDDEN_IP) : ircd_ntoa((const char*) &(cli_ip(acptr))); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_HOS)) { char *p2 = (IsViewip(sptr) || acptr == sptr) ? get_realhost(acptr) : get_virtualhost(acptr); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_SER)) { char *p2; int haspriv = IsAnOper(sptr) || es_representante(sptr); if (IsMe(cli_user(acptr)->server)) { if ((sptr != acptr) && feature_bool(FEAT_HIS_WHO_SERVERNAME) && !haspriv) p2 = (char *)feature_str(FEAT_HIS_SERVERNAME); else p2 = cli_name(&me); } else { if (IsHiddenserv(cli_user(acptr)->server) && !haspriv) p2 = (char *)feature_str(FEAT_HIS_SERVERNAME); else p2 = cli_name(cli_user(acptr)->server); } *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_NIC)) { char *p2 = cli_name(acptr); *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } if (!fields || (fields & WHO_FIELD_FLA)) { *(p1++) = ' '; if (cli_user(acptr)->away) *(p1++) = 'G'; else *(p1++) = 'H'; if ((IsAnOper(acptr) || es_representante(acptr) || IsPreoper(acptr))/* && (HasPriv(acptr, PRIV_DISPLAY) || HasPriv(sptr, PRIV_SEE_OPERS))*/) *(p1++) = '*'; if (fields) { /* If you specified flags then we assume you know how to parse * multiple channel status flags, as this is currently the only * way to know if someone has @'s *and* is +'d. */ if (chptr && is_chan_op(acptr, chptr)) *(p1++) = '@'; if (chptr && has_voice(acptr, chptr)) *(p1++) = '+'; if (chptr && is_chan_halfop(acptr, chptr)) *(p1++) = '%'; if (chptr && is_chan_owner(acptr, chptr)) *(p1++) = '.'; if (chptr && is_zombie(acptr, chptr)) *(p1++) = '!'; } else { if (chptr && is_chan_op(acptr, chptr)) *(p1++) = '@'; else if (chptr && has_voice(acptr, chptr)) *(p1++) = '+'; else if (chptr && is_zombie(acptr, chptr)) *(p1++) = '!'; else if (chptr && is_chan_halfop(acptr, chptr)) *(p1++) = '%'; else if (chptr && is_chan_owner(acptr, chptr)) *(p1++) = '.'; } if (IsDeaf(acptr)) *(p1++) = 'd'; if (IsAnOper(sptr)) { if (IsInvisible(acptr)) *(p1++) = 'i'; if (SendWallops(acptr)) *(p1++) = 'w'; if (SendDebug(acptr)) *(p1++) = 'g'; } if (HasHiddenHost(acptr)) *(p1++) = 'x'; if (IsAnOper(acptr)) *(p1++) = 'o'; if (IsPreoper(acptr)) *(p1++) = 'p'; if (IsHelpOp(acptr)) *(p1++) = 'h'; if (IsCoadmin(acptr)) *(p1++) = 'a'; if (IsAdmin(acptr)) *(p1++) = 'A'; if (IsDevel(acptr)) *(p1++) = 'D'; if (IsViewip(acptr)) *(p1++) = 'X'; } if (!fields || (fields & WHO_FIELD_DIS)) { *p1++ = ' '; if (!fields) *p1++ = ':'; /* Place colon here for default reply */ if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr)) *p1++ = (sptr == acptr) ? '0' : '3'; else /* three digit hopcount maximum */ p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr)); } if (fields & WHO_FIELD_IDL) { *p1++ = ' '; if (MyUser(acptr)) { p1 += ircd_snprintf(0, p1, 11, "%d", CurrentTime - cli_user(acptr)->last); } else { *p1++ = '0'; } } if (!fields || (fields & WHO_FIELD_REN)) { char *p2 = cli_info(acptr); *p1++ = ' '; if (fields) *p1++ = ':'; /* Place colon here for special reply */ while ((*p2) && (*(p1++) = *(p2++))); } if (fields & WHO_FIELD_ACC) { char *p2 = cli_user(acptr)->account; *(p1++) = ' '; while ((*p2) && (*(p1++) = *(p2++))); } /* The first char will always be an useless blank and we need to terminate buf1 */ *p1 = '\0'; p1 = buf1; send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1); }
/* * Send whois information for acptr to sptr */ static void do_whois(struct Client* sptr, struct Client *acptr, int parc) { struct Client *a2cptr=0; struct Channel *chptr=0; int mlen; int len; static char buf[512]; const struct User* user = cli_user(acptr); const char* name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr); a2cptr = feature_bool(FEAT_HIS_WHOIS_SERVERNAME) && !IsAnOper(sptr) && sptr != acptr ? &his : user->server; assert(user); send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host, cli_info(acptr)); /* Display the channels this user is on. */ if (!IsChannelService(acptr)) { struct Membership* chan; mlen = strlen(cli_name(&me)) + strlen(cli_name(sptr)) + 12 + strlen(name); len = 0; *buf = '\0'; for (chan = user->channel; chan; chan = chan->next_channel) { chptr = chan->channel; if (!ShowChannel(sptr, chptr) && !(IsOper(sptr) && IsLocalChannel(chptr->chname))) continue; if (acptr != sptr && IsZombie(chan)) continue; /* Don't show local channels when HIS is defined, unless it's a * remote WHOIS --ULtimaTe_ */ if (IsLocalChannel(chptr->chname) && (acptr != sptr) && (parc == 2) && feature_bool(FEAT_HIS_WHOIS_LOCALCHAN) && !IsAnOper(sptr)) continue; if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5) { send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf); *buf = '\0'; len = 0; } if (IsDeaf(acptr)) *(buf + len++) = '-'; if (!ShowChannel(sptr, chptr)) *(buf + len++) = '*'; if (IsDelayedJoin(chan) && (sptr != acptr)) *(buf + len++) = '<'; else if (IsChanOp(chan)) *(buf + len++) = '@'; else if (HasVoice(chan)) *(buf + len++) = '+'; else if (IsZombie(chan)) *(buf + len++) = '!'; if (len) *(buf + len) = '\0'; strcpy(buf + len, chptr->chname); len += strlen(chptr->chname); strcat(buf + len, " "); len++; } if (buf[0] != '\0') send_reply(sptr, RPL_WHOISCHANNELS, name, buf); } send_reply(sptr, RPL_WHOISSERVER, name, cli_name(a2cptr), cli_info(a2cptr)); if (user) { if (user->away) send_reply(sptr, RPL_AWAY, name, user->away); if (SeeOper(sptr,acptr)) send_reply(sptr, RPL_WHOISOPERATOR, name); if (IsAccount(acptr)) send_reply(sptr, RPL_WHOISACCOUNT, name, user->account); if (HasHiddenHost(acptr) && (IsAnOper(sptr) || acptr == sptr)) send_reply(sptr, RPL_WHOISACTUALLY, name, user->username, user->realhost, ircd_ntoa(&cli_ip(acptr))); /* Hint: if your looking to add more flags to a user, eg +h, here's * probably a good place to add them :) */ if (MyConnect(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) || (sptr == acptr || IsAnOper(sptr) || parc >= 3))) send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last, cli_firsttime(acptr)); } }