/************************************************************************ * IRC - Internet Relay Chat, modules/m_who.c * * Copyright (C) 2000-2003 TR-IRCD Development * * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Co Center * * See file AUTHORS in IRC package for additional names of * the programmers. * * This program is free softwmare; 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 2, 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 "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "msg.h" #include "channel.h" #include "s_conf.h" #include "language.h" #include "usermode.h" #include "h.h" static struct Message _msgtab[] = { {MSG_WHO, 0, MAXPARA, M_SLOW, 0L, m_unregistered, m_who, m_who, m_ignore, m_ignore} }; #ifndef STATIC_MODULES char *_version = "$Revision: 1.4 $"; void _modinit(void) { mod_add_cmd(_msgtab); } void _moddeinit(void) { mod_del_cmd(_msgtab); } #else void m_who_init(void) { mod_add_cmd(_msgtab); } #endif /* We do not need link traversal here, because users * do not exist in the root channel, and it is already * set to +tnL, after cleaning. -TimeMr14C */ SOpts wsopts; int build_searchopts(aClient *, int, char **); int chk_who(aClient *, int); int build_searchopts(aClient *sptr, int parc, char *parv[]) { static char *who_help[] = { "/WHO [+|-][acghmnsu] [args]", "Flags are specified like channel modes, the flags cgmnsu all have arguments", "Flags are set to a positive check by +, a negative check by -", "The flags work as follows:", "Flag a: user is away", "Flag c : user is on ,", " no wildcards accepted", "Flag g : user has string in their GCOS,", " wildcards accepted, oper only", "Flag h : user has string in their hostname,", " wildcards accepted", "Flag i : user is from wildcards accepted,", "Flag f : user has string in their", " hostname output.", "Flag m : user has set on them,", " only o/A/a for nonopers", "Flag n : user has string in their nickname,", " wildcards accepted", "Flag s : user is on server ,", " wildcards not accepted", "Flag u : user has string in their username,", " wildcards accepted", "Flag L : user has string as their language,", NULL }; char *flags, change = 1, *s; int args = 1; memset((char *) &wsopts, '\0', sizeof(SOpts)); /* * if we got no extra arguments, send them the help. yeech. * if it's /who ?, send them the help */ if (parc < 1 || parv[0][0] == '?') { char **ptr = who_help; for (; *ptr; ptr++) send_me_numeric(sptr, RPL_COMMANDSYNTAX, *ptr); send_me_numeric(sptr, RPL_ENDOFWHO, "?"); return 0; } /* * backwards compatibility */ else if (parv[0][0] == '0' && parv[0][1] == 0) { if (parc > 1 && *parv[1] == 'o') { wsopts.check_umode = 1; wsopts.umode_plus = 1; wsopts.umodes = UMODE_o; } wsopts.host_plus = 1; wsopts.host = "*"; return 1; } /* * if the first argument isn't a list of stuff */ else if (parv[0][0] != '+' && parv[0][0] != '-') { if (parv[0][0] == '#' || parv[0][0] == '&') { wsopts.channel = find_channel(parv[0]); if (wsopts.channel == NULL) { send_me_numeric(sptr, ERR_NOSUCHCHANNEL, parv[0]); return 0; } } else { /* * If the arguement has a . in it, treat it as an * address. Otherwise treat it as a nick. -Rak */ if (strchr(parv[0], '.')) { wsopts.host_plus = 1; wsopts.host = parv[0]; } else { wsopts.nick_plus = 1; wsopts.nick = parv[0]; } } return 1; } /* * now walk the list (a lot like set_mode) and set arguments * as appropriate. */ flags = parv[0]; wsopts.langnum = 0; while (*flags) { switch (*flags) { case '+': case '-': change = (*flags == '+' ? 1 : 0); break; case 'a': if (change) wsopts.away_plus = 1; /* they want here people */ else wsopts.away_plus = 0; wsopts.check_away = 1; break; case 'c': if (parv[args] == NULL || !change) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.channel = find_channel(parv[args]); if (wsopts.channel == NULL) { send_me_numeric(sptr, ERR_NOSUCHCHANNEL, parv[args]); return 0; } wsopts.chan_plus = change; args++; break; case 'f': if (parv[args] == NULL) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.fakehost = parv[args]; wsopts.fhost_plus = change; args++; break; case 'g': if (parv[args] == NULL || !IsAnOper(sptr)) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.gcos = parv[args]; wsopts.gcos_plus = change; args++; break; case 'h': if (parv[args] == NULL) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.host = parv[args]; wsopts.host_plus = change; args++; break; case 'i': if (parv[args] == NULL || !IsAnOper(sptr)) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.ip = parv[args]; wsopts.ip_plus = change; args++; break; case 'L': if (parv[args] == NULL) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.lang = parv[args]; wsopts.langnum = lang_parse(parv[args]); wsopts.lang_plus = change; args++; break; case 'm': if (parv[args] == NULL) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } s = parv[args]; while (*s) { if (umodetab[(int) *s].in_use) wsopts.umodes |= umodetab[(int) *s].type; s++; } if (!IsAnOper(sptr)) wsopts.umodes = (wsopts.umodes & (UMODE_o | UMODE_a | UMODE_A)); wsopts.umode_plus = change; if (wsopts.umodes) wsopts.check_umode = 1; args++; break; case 'n': if (parv[args] == NULL) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.nick = parv[args]; wsopts.nick_plus = change; args++; break; case 's': if (parv[args] == NULL || !change) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.server = find_server(parv[args]); if (wsopts.server == NULL) { send_me_numeric(sptr, ERR_NOSUCHSERVER, parv[args]); return 0; } wsopts.serv_plus = change; args++; break; case 'u': if (parv[args] == NULL) { send_me_numeric(sptr, ERR_WHOSYNTAX); return 0; } wsopts.user = parv[args]; wsopts.user_plus = change; args++; break; } flags++; } return 1; } /* * these four are used by chk_who to check gcos/nick/user/host/ip/fakehost * respectively */ int (*gchkfn) (char *, char *); int (*nchkfn) (char *, char *); int (*uchkfn) (char *, char *); int (*hchkfn) (char *, char *); int (*ichkfn) (char *, char *); int (*lchkfn) (int, int); int (*fchkfn) (char *, char *); int chk_who(aClient *ac, int showall) { if (!IsPerson(ac)) return 0; if (IsInvisible(ac) && !showall) return 0; if (wsopts.check_umode) if ((wsopts.umode_plus && !((ac->umode & wsopts.umodes) == wsopts.umodes)) || (!wsopts.umode_plus && ((ac->umode & wsopts.umodes) == wsopts.umodes))) return 0; if (wsopts.check_away) if ((wsopts.away_plus && ac->user->away == NULL) || (!wsopts.away_plus && ac->user->away != NULL)) return 0; /* * while this is wasteful now, in the future * when clients contain pointers to their servers * of origin, this'll become a 4 byte check instead of a irc_strcmp * -wd * * welcome to the future... :) * -lucas */ if (wsopts.serv_plus) if (wsopts.server != ac->servptr) return 0; /* * we only call match once, since if the first condition * isn't true, most (all?) compilers will never try the * second...phew :) */ if (wsopts.user != NULL) if ((wsopts.user_plus && uchkfn(wsopts.user, ac->user->username)) || (!wsopts.user_plus && !uchkfn(wsopts.user, ac->user->username))) return 0; if (wsopts.nick != NULL) if ((wsopts.nick_plus && nchkfn(wsopts.nick, ac->name)) || (!wsopts.nick_plus && !nchkfn(wsopts.nick, ac->name))) return 0; if (wsopts.host != NULL) if ((wsopts.host_plus && hchkfn(wsopts.host, ac->user->host)) || (!wsopts.host_plus && !hchkfn(wsopts.host, ac->user->host))) return 0; if (wsopts.fakehost != NULL) if ((wsopts.fhost_plus && fchkfn(wsopts.fakehost, ac->user->fakehost)) || (!wsopts.fhost_plus && !fchkfn(wsopts.fakehost, ac->user->fakehost))) return 0; if (wsopts.ip != NULL) if ((wsopts.ip_plus && ichkfn(wsopts.ip, ac->hostip)) || (!wsopts.ip_plus && !ichkfn(wsopts.ip, ac->hostip))) return 0; if (wsopts.gcos != NULL) if ((wsopts.gcos_plus && gchkfn(wsopts.gcos, ac->info)) || (!wsopts.gcos_plus && !gchkfn(wsopts.gcos, ac->info))) return 0; if (wsopts.langnum) if ((wsopts.lang_plus && lchkfn(wsopts.langnum, ac->lang)) || (!wsopts.lang_plus && !lchkfn(wsopts.langnum, ac->lang))) return 0; return 1; } static inline char *first_visible_channel(aClient *cptr, aClient *sptr) { dlink_node *lp; int secret = 0; aChannel *chptr = NULL; static char chnbuf[CHANNELLEN + 2]; if (cptr->user->channel.head) { if (IsSeeHidden(sptr)) { chptr = cptr->user->channel.head->data; if (!(ShowChannelNames(sptr, chptr))) secret = 1; } else { for (lp = cptr->user->channel.head; lp; lp = lp->next) { if (ShowChannelNames(sptr, (aChannel *) lp->data)) break; } if (lp) chptr = lp->data; } if (chptr) { if (!secret) return chptr->chname; ircsprintf(chnbuf, "%%%s", chptr->chname); return chnbuf; } } return "*"; } /* * allow lusers only 200 replies from /who */ #define MAXWHOREPLIES 200 #define WHO_HOPCOUNT(s, a) ((IsULine((a)) && !IsAnOper((s))) ? 0 : a->hopcount) int m_who(aClient *cptr, aClient *sptr, int parc, char *parv[]) { struct ChanMember *cm; aClient *ac; dlink_node *ptr; int shown = 0, i = 0, showall = IsSeeHidden(sptr); char out[HOSTLEN]; char status[4]; if (!MyClient(sptr)) return 0; if (!build_searchopts(sptr, parc - 1, parv + 1)) return 0; /* /who was no good */ if (wsopts.gcos != NULL && (strchr(wsopts.gcos, '?')) == NULL && (strchr(wsopts.gcos, '*')) == NULL) gchkfn = irc_strcmp; else gchkfn = match; if (wsopts.nick != NULL && (strchr(wsopts.nick, '?')) == NULL && (strchr(wsopts.nick, '*')) == NULL) nchkfn = irc_strcmp; else nchkfn = match; if (wsopts.user != NULL && (strchr(wsopts.user, '?')) == NULL && (strchr(wsopts.user, '*')) == NULL) uchkfn = irc_strcmp; else uchkfn = match; if (wsopts.host != NULL && (strchr(wsopts.host, '?')) == NULL && (strchr(wsopts.host, '*')) == NULL) hchkfn = irc_strcmp; else hchkfn = match; if (wsopts.fakehost != NULL && (strchr(wsopts.fakehost, '?')) == NULL && (strchr(wsopts.fakehost, '*')) == NULL) fchkfn = irc_strcmp; else fchkfn = match; if (wsopts.ip != NULL && (strchr(wsopts.ip, '?')) == NULL && (strchr(wsopts.ip, '*')) == NULL) ichkfn = irc_strcmp; else ichkfn = match; if (wsopts.langnum) lchkfn = irc_equal; if (wsopts.channel != NULL) { if (IsMember(sptr, wsopts.channel)) showall = 1; else if (SecretChannel(wsopts.channel) && IsSeeHidden(sptr)) showall = 1; else if (NamesChannel(wsopts.channel) && IsSeeHidden(sptr)) showall = 1; else if (!SecretChannel(wsopts.channel) && !NamesChannel(wsopts.channel) && IsAnOper(sptr)) showall = 1; else showall = 0; if (showall || !SecretChannel(wsopts.channel) || !NamesChannel(wsopts.channel)) { for (ptr = wsopts.channel->members.head; ptr; ptr = ptr->next) { cm = ptr->data; if (!cm) continue; ac = cm->client_p; i = 0; if (!chk_who(ac, showall)) continue; /* * wow, they passed it all, give them the reply... * IF they haven't reached the max, or they're an oper */ status[i++] = (ac->user->away == NULL ? 'H' : 'G'); status[i] = (IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsAnOper(sptr)) ? '%' : 0)); if (!IsChanHideOps(wsopts.channel) || IsSeeHidden(sptr)) { status[((status[i]) ? ++i : i)] = (IsChanUser(ac, wsopts.channel, CHFL_CHANOP) ? '@' : IsChanUser(ac, wsopts.channel, CHFL_VOICE) ? '+' : 0); } status[++i] = 0; send_me_numeric(sptr, RPL_WHOREPLY, wsopts.channel->chname, ac->user->username, IsFake(ac) ? ac->user->fakehost : ac->user->host, ((WillHideName(ac->from) || (MyClient(ac) && ServerHide.enable)) ? stealth_server(ac->user->server, out) : ac->user->server), ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info); } } send_me_numeric(sptr, RPL_ENDOFWHO, wsopts.channel->chname); return 0; } /* * if (for whatever reason) they gave us a nick with no * wildcards, just do a find_person, bewm! */ else if (nchkfn == irc_strcmp) { ac = find_person(wsopts.nick); if (ac != NULL) { if (!chk_who(ac, 1)) { send_me_numeric(sptr, RPL_ENDOFWHO, wsopts.host != NULL ? wsopts.host : wsopts.nick); return 0; } else { status[0] = (ac->user->away == NULL ? 'H' : 'G'); status[1] = (IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsAnOper(sptr)) ? '%' : 0)); status[2] = 0; send_me_numeric(sptr, RPL_WHOREPLY, (!IsWhoisHideChan(ac)) ? first_visible_channel(ac, sptr) : "*", ac->user->username, IsFake(ac) ? ac->user->fakehost : ac->user->host, ((WillHideName(ac->from) || (MyClient(ac) && ServerHide.enable)) ? stealth_server(ac->user->server, out) : ac->user->server), ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info); send_me_numeric(sptr, RPL_ENDOFWHO, wsopts.host != NULL ? wsopts.host : wsopts.nick); return 0; } } send_me_numeric(sptr, RPL_ENDOFWHO, wsopts.host != NULL ? wsopts.host : wsopts.nick); return 0; } for (ac = GlobalClientList; ac; ac = ac->next) { if (!chk_who(ac, showall)) continue; /* * wow, they passed it all, give them the reply... * IF they haven't reached the max, or they're an oper */ if (shown == MAXWHOREPLIES && !IsAnOper(sptr)) { send_me_numeric(sptr, ERR_WHOLIMEXCEED, MAXWHOREPLIES); break; /* break out of loop so we can send end of who */ } status[0] = (ac->user->away == NULL ? 'H' : 'G'); status[1] = (IsAnOper(ac) ? '*' : ((IsInvisible(ac) && IsAnOper(sptr)) ? '%' : 0)); status[2] = 0; send_me_numeric(sptr, RPL_WHOREPLY, (!IsWhoisHideChan(ac)) ? first_visible_channel(ac, sptr) : "*", ac->user->username, IsFake(ac) ? ac->user->fakehost : ac->user->host, ((WillHideName(ac->from) || (MyClient(ac) && ServerHide.enable)) ? stealth_server(ac->user->server, out) : ac->user->server), ac->name, status, WHO_HOPCOUNT(sptr, ac), ac->info); shown++; } send_me_numeric(sptr, RPL_ENDOFWHO, (wsopts.host != NULL ? wsopts.host : (wsopts.nick != NULL ? wsopts.nick : (wsopts.user != NULL ? wsopts.user : (wsopts.gcos != NULL ? wsopts.gcos : (wsopts.server != NULL ? wsopts.server->name : (wsopts.fakehost != NULL ? wsopts.fakehost : (wsopts.langnum ? wsopts.lang : "*")))))))); return 0; }