/** Handle a local user's attempt to get or set a channel topic. * * \a parv has the following elements: * \li \a parv[1] is the channel name * \li \a parv[\a parc - 1] is the topic (if \a parc > 2) * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int m_topic(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; char *topic = 0, *name, *p = 0; if (parc < 2) return need_more_params(sptr, "TOPIC"); if (parc > 2) topic = parv[parc - 1]; for (; (name = ircd_strtok(&p, parv[1], ",")); parv[1] = 0) { chptr = 0; /* Does the channel exist */ if (!IsChannelName(name) || !(chptr = FindChannel(name))) { send_reply(sptr,ERR_NOSUCHCHANNEL,name); continue; } /* Trying to check a topic outside a secret channel */ if ((topic || SecretChannel(chptr)) && !find_channel_member(sptr, chptr)) { send_reply(sptr, ERR_NOTONCHANNEL, chptr->chname); continue; } /* only asking for topic */ if (!topic) { if (chptr->topic[0] == '\0') send_reply(sptr, RPL_NOTOPIC, chptr->chname); else { send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic); send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick, chptr->topic_time); } } #if defined(DDB) || defined(SERVICES) else if ((chptr->mode.mode & MODE_TOPICLIMIT) && !(is_chan_owner(sptr, chptr) || is_chan_op(sptr, chptr))) #else else if ((chptr->mode.mode & MODE_TOPICLIMIT) && !is_chan_op(sptr, chptr)) #endif send_reply(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname); else if (!client_can_send_to_channel(sptr, chptr, 1)) send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname); else do_settopic(sptr,cptr,chptr,topic,0); } return 0; }
/** Set a channel topic or report an error. * @param[in] sptr Original topic setter. * @param[in] cptr Neighbor that sent the topic message. * @param[in] chptr Channel to set topic on. * @param[in] topic New topic. * @param[in] ts Timestamp that topic was set (0 for current time). */ static void do_settopic(struct Client *sptr, struct Client *cptr, struct Channel *chptr, char *topic, time_t ts) { struct Client *from; int newtopic; if (feature_bool(FEAT_HIS_BANWHO) && IsServer(sptr)) from = &his; else from = sptr; /* Note if this is just a refresh of an old topic, and don't * send it to all the clients to save bandwidth. We still send * it to other servers as they may have split and lost the topic. */ newtopic=ircd_strncmp(chptr->topic,topic,TOPICLEN)!=0; /* setting a topic */ ircd_strncpy(chptr->topic, topic, TOPICLEN); ircd_strncpy(chptr->topic_nick, cli_name(from), NICKLEN); chptr->topic_time = ts ? ts : TStime(); /* Fixed in 2.10.11: Don't propagate local topics */ if (!IsLocalChannel(chptr->chname)) sendcmdto_serv(sptr, CMD_TOPIC, cptr, "%H %Tu %Tu :%s", chptr, chptr->creationtime, chptr->topic_time, chptr->topic); if (newtopic) { struct Membership *member; /* If the member is delayed-join, show them. */ member = find_channel_member(sptr, chptr); if (member && IsDelayedJoin(member)) RevealDelayedJoin(member); sendcmdto_channel(from, CMD_TOPIC, chptr, NULL, SKIP_SERVERS, "%H :%s", chptr, chptr->topic); } /* if this is the same topic as before we send it to the person that * set it (so they knew it went through ok), but don't bother sending * it to everyone else on the channel to save bandwidth */ else if (MyUser(sptr)) sendcmdto_one(sptr, CMD_TOPIC, sptr, "%H :%s", chptr, chptr->topic); }
/* * ms_invite - server message handler * * parv[0] - sender prefix * parv[1] - user to invite * parv[2] - channel name * parv[3] - (optional) channel timestamp * * - INVITE now is accepted only if who does it is chanop (this of course * implies that channel must exist and he must be on it). * * - On the other side it IS processed even if channel is NOT invite only * leaving room for other enhancements like inviting banned ppl. -- Nemesi * * - Invite with no parameters now lists the channels you are invited to. * - Isomer 23 Oct 99 * * - Invite with too-late timestamp, or with no timestamp from a bursting * server, is silently discarded. - Entrope 19 Jan 05 */ int ms_invite(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; struct Channel *chptr; time_t invite_ts; if (IsServer(sptr)) { /* * this will blow up if we get an invite from a server * we look for channel membership in sptr below. */ return protocol_violation(sptr,"Server attempting to invite"); } if (parc < 3 || EmptyString(parv[2])) { /* * should have been handled upstream, ignore it. */ protocol_violation(sptr,"Too few arguments to invite"); return need_more_params(sptr,"INVITE"); } if (!IsGlobalChannel(parv[2])) { /* * should not be sent */ return protocol_violation(sptr, "Invite to a non-standard channel %s",parv[2]); } if (!(acptr = FindUser(parv[1]))) { send_reply(sptr, ERR_NOSUCHNICK, parv[1]); return 0; } if (!(chptr = FindChannel(parv[2]))) { /* * allow invites to non existent channels, bleah * avoid JOIN, INVITE, PART abuse */ sendcmdto_one(sptr, CMD_INVITE, acptr, "%C :%s", acptr, parv[2]); return 0; } if (parc > 3) { invite_ts = atoi(parv[3]); if (invite_ts > chptr->creationtime) return 0; } else if (IsBurstOrBurstAck(cptr)) return 0; if (!IsChannelService(sptr) && !find_channel_member(sptr, chptr)) { send_reply(sptr, ERR_NOTONCHANNEL, chptr->chname); return 0; } if (find_channel_member(acptr, chptr)) { send_reply(sptr, ERR_USERONCHANNEL, cli_name(acptr), chptr->chname); return 0; } if (is_silenced(sptr, acptr)) return 0; if (MyConnect(acptr)) { add_invite(acptr, chptr); sendcmdto_one(sptr, CMD_INVITE, acptr, "%s %H", cli_name(acptr), chptr); } else { sendcmdto_one(sptr, CMD_INVITE, acptr, "%s %H %Tu", cli_name(acptr), chptr, chptr->creationtime); } if (feature_bool(FEAT_ANNOUNCE_INVITES)) { /* Announce to channel operators. */ sendcmdto_channel_butserv_butone(&his, get_error_numeric(RPL_ISSUEDINVITE)->str, NULL, chptr, sptr, SKIP_NONOPS, "%H %C %C :%C has been invited by %C", chptr, acptr, sptr, acptr, sptr); /* Announce to servers with channel operators. */ sendcmdto_channel_servers_butone(sptr, NULL, TOK_INVITE, chptr, acptr, SKIP_NONOPS, "%s %H %Tu", cli_name(acptr), chptr, chptr->creationtime); } return 0; }
/* * m_invite - generic message handler * * parv[0] - sender prefix * parv[1] - user to invite * parv[2] - channel name * * - INVITE now is accepted only if who does it is chanop (this of course * implies that channel must exist and he must be on it). * * - On the other side it IS processed even if channel is NOT invite only * leaving room for other enhancements like inviting banned ppl. -- Nemesi * * - Invite with no parameters now lists the channels you are invited to. * - Isomer 23 Oct 99 */ int m_invite(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Client *acptr; struct Channel *chptr; if (parc < 2 ) { /* * list the channels you have an invite to. */ struct SLink *lp; for (lp = cli_user(sptr)->invited; lp; lp = lp->next) send_reply(cptr, RPL_INVITELIST, lp->value.chptr->chname); send_reply(cptr, RPL_ENDOFINVITELIST); return 0; } if (parc < 3 || EmptyString(parv[2])) return need_more_params(sptr, "INVITE"); if (!(acptr = FindUser(parv[1]))) { send_reply(sptr, ERR_NOSUCHNICK, parv[1]); return 0; } if (is_silenced(sptr, acptr)) return 0; if (!IsChannelName(parv[2]) || !strIsIrcCh(parv[2]) || !(chptr = FindChannel(parv[2]))) { send_reply(sptr, ERR_NOSUCHCHANNEL, parv[2]); return 0; } if (!find_channel_member(sptr, chptr)) { send_reply(sptr, ERR_NOTONCHANNEL, chptr->chname); return 0; } if (find_channel_member(acptr, chptr)) { send_reply(sptr, ERR_USERONCHANNEL, cli_name(acptr), chptr->chname); return 0; } if (!is_chan_op(sptr, chptr)) { send_reply(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname); return 0; } /* If we get here, it was a VALID and meaningful INVITE */ if (check_target_limit(sptr, acptr, cli_name(acptr), 0)) return 0; send_reply(sptr, RPL_INVITING, cli_name(acptr), chptr->chname); if (cli_user(acptr)->away) send_reply(sptr, RPL_AWAY, cli_name(acptr), cli_user(acptr)->away); if (MyConnect(acptr)) { add_invite(acptr, chptr); sendcmdto_one(sptr, CMD_INVITE, acptr, "%s %H", cli_name(acptr), chptr); } else if (!IsLocalChannel(chptr->chname)) { sendcmdto_one(sptr, CMD_INVITE, acptr, "%s %H %Tu", cli_name(acptr), chptr, chptr->creationtime); } if (!IsLocalChannel(chptr->chname) || MyConnect(acptr)) { if (feature_bool(FEAT_ANNOUNCE_INVITES)) { /* Announce to channel operators. */ sendcmdto_channel_butserv_butone(&his, get_error_numeric(RPL_ISSUEDINVITE)->str, NULL, chptr, sptr, SKIP_NONOPS, "%H %C %C :%C has been invited by %C", chptr, acptr, sptr, acptr, sptr); /* Announce to servers with channel operators. */ sendcmdto_channel_servers_butone(sptr, NULL, TOK_INVITE, chptr, acptr, SKIP_NONOPS, "%s %H %Tu", cli_name(acptr), chptr, chptr->creationtime); } } return 0; }
int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct Channel *chptr; struct Channel *ch2ptr; struct Client *c2ptr; struct Membership* member; char* s; char* para = parc > 1 ? parv[1] : 0; int showingdelayed = 0; if (parc > 1 && !ircd_strcmp(parv[1], "-D")) { para = (parc > 2) ? parv[2] : 0; showingdelayed = NAMES_DEL; if (parc > 3 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %s %C", 3, parc, parv)) return 0; } else if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv)) return 0; if (EmptyString(para)) { send_reply(sptr, RPL_ENDOFNAMES, "*"); return 0; } do { s = strchr(para, ','); if (s) *s++ = '\0'; /* * Special Case 1: "/names 0". * Full list as per RFC. */ if ((*para == '0') || (*para == '\0')) { int idx; int mlen; int flag; struct Channel *ch3ptr; char buf[BUFSIZE]; mlen = strlen(cli_name(&me)) + 10 + strlen(cli_name(sptr)); /* List all visible channels/visible members */ for (ch2ptr = GlobalChannelList; ch2ptr; ch2ptr = ch2ptr->next) { if (!ShowChannel(sptr, ch2ptr)) continue; /* Don't show secret chans. */ else if (find_channel_member(sptr, ch2ptr)) do_names(sptr, ch2ptr, showingdelayed|NAMES_ALL); /* Full list if we're in this chan. */ else do_names(sptr, ch2ptr, showingdelayed|NAMES_VIS); } /* List all remaining users on channel '*' */ strcpy(buf, "* * :"); idx = 5; flag = 0; for (c2ptr = GlobalClientList; c2ptr; c2ptr = cli_next(c2ptr)) { int showflag = 0; if (!IsUser(c2ptr) || (sptr != c2ptr && IsInvisible(c2ptr))) continue; member = cli_user(c2ptr)->channel; while (member) { ch3ptr = member->channel; if (PubChannel(ch3ptr) || find_channel_member(sptr, ch3ptr)) showflag = 1; member = member->next_channel; } if (showflag) /* Have we already shown them? */ continue; strcpy(buf + idx, cli_name(c2ptr)); idx += strlen(cli_name(c2ptr)); buf[idx++] = ' '; flag = 1; if (mlen + idx + NICKLEN + 3 > BUFSIZE) /* space, \r\n\0 */ { send_reply(sptr, RPL_NAMREPLY, buf); strcpy(buf, "* * :"); idx = 5; flag = 0; } } if (flag) send_reply(sptr, RPL_NAMREPLY, buf); send_reply(sptr, RPL_ENDOFNAMES, "*"); } else if ((chptr = FindChannel(para)) != NULL) { member = find_member_link(chptr, sptr); if (member) { /* * Special Case 2: User is on this channel, requesting full names list. * (As performed with each /join) - ** High frequency usage ** */ do_names(sptr, chptr, showingdelayed|NAMES_ALL|NAMES_EON); } else { /* * Special Case 3: User isn't on this channel, show all visible users, in * non secret channels. */ do_names(sptr, chptr, showingdelayed|NAMES_VIS|NAMES_EON); } } else send_reply(sptr, RPL_ENDOFNAMES, para); } while ((para = s) != NULL); return 1; }
/* * 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; }
/* * Search and return as many people as matched by the wild 'nick'. * returns the number of people found (or, obviously, 0, if none where * found). */ static int do_wilds(struct Client* sptr, char *nick, int count, int parc) { struct Client *acptr; /* Current client we're considering */ struct User *user; /* the user portion of the client */ const char *name; /* the name of this client */ struct Membership* chan; int invis; /* does +i apply? */ int member; /* Is this user on any channels? */ int showperson; /* Should we show this person? */ int found = 0 ; /* How many were found? */ /* Ech! This is hideous! */ for (acptr = GlobalClientList; (acptr = next_client(acptr, nick)); acptr = cli_next(acptr)) { if (!IsRegistered(acptr)) continue; if (IsServer(acptr)) continue; /* * I'm always last :-) and acptr->next == 0!! * * Isomer: Does this strike anyone else as being a horrible hideous * hack? */ if (IsMe(acptr)) { assert(!cli_next(acptr)); break; } /* * 'Rules' established for sending a WHOIS reply: * * - if wildcards are being used don't send a reply if * the querier isn't any common channels and the * client in question is invisible. * * - only send replies about common or public channels * the target user(s) are on; */ user = cli_user(acptr); name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr); assert(user); invis = (acptr != sptr) && IsInvisible(acptr); member = (user && user->channel) ? 1 : 0; showperson = !invis && !member; /* Should we show this person now? */ if (showperson) { found++; do_whois(sptr, acptr, parc); if (count+found>MAX_WHOIS_LINES) return found; continue; } /* Step through the channels this user is on */ for (chan = user->channel; chan; chan = chan->next_channel) { struct Channel *chptr = chan->channel; /* If this is a public channel, show the person */ if (!invis && PubChannel(chptr)) { showperson = 1; break; } /* if this channel is +p and not +s, show them */ if (!invis && HiddenChannel(chptr) && !SecretChannel(chptr)) { showperson = 1; break; } member = find_channel_member(sptr, chptr) ? 1 : 0; if (invis && !member) continue; /* If sptr isn't really on this channel, skip it */ if (IsZombie(chan)) continue; /* Is this a common channel? */ if (member) { showperson = 1; break; } } /* of for (chan in channels) */ /* Don't show this person */ if (!showperson) continue; do_whois(sptr, acptr, parc); found++; if (count+found>MAX_WHOIS_LINES) return found; } /* of global client list */ return found; }