Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

m_who.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: m_who.c,v 1.39.2.2 2005/01/15 23:53:31 amcwilliam Exp $
 */

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "h.h"
#include "memory.h"
#include "modules.h"
#include "xmode.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

Module MOD_HEADER(m_who) = {
      "m_who",
      "/WHO command",
      6, "$Revision: 1.39.2.2 $"
};

int MOD_LOAD(m_who)()
{
      if (register_command(&MOD_HEADER(m_who), &CMD_WHO, m_who) == NULL) {
            return MOD_FAILURE;
      }
      return MOD_SUCCESS;
}

int MOD_UNLOAD(m_who)()
{
      return MOD_SUCCESS;
}

static char *who_oper_help[] = {
      "/WHO [+|-][acghimnsuzCMHI] [args]",
      "Flags are specified like channel modes,",
      "The flags cghimnsuz 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 o:             user is oper",
      " Flag c <channel>:   user is on <channel>,",
      "                     no wildcards accepted",
      " Flag g <gcos>:      user has string <gcos> in their GCOS,",
      "                     wildcards accepted",
      " Flag h <host>:      user has string <host> in their hostname,",
      "                     wildcards accepted",
      " Flag i <ip>:        user is from <ip>, wildcards accepted,",
      " Flag m <usermodes>: user has <usermodes> set on them",
      " Flag n <nick>:      user has string <nick> in their nickname,",
      "                     wildcards accepted",
      " Flag s <server>:    user is on server <server>,",
      "                     wildcards not accepted",
      " Flag u <user>:      user has string <user> in their username,",
      "                     wildcards accepted",
      " ",
      "Behavior flags:",
      " Flag C: show first visible channel user is in",
      " Flag M: check for user in channels I am a member of",
      " Flag H: always show real hosts",
      " Flag I: always show IPs instead of hosts",
      " ",
      "Returned flags:",
      " Flag H: user is here (not away)",
      " Flag G: user is gone (away)",
      " Flag r: users' nickname is registered",
      " Flag z: user is using a secure connection (SSL)",
      " Flag *: user is a server operator",
      " Flag !: user is invisible (+i)",
      " Flag @: user is a channel operator",
      " Flag %: user is a half operator",
      " Flag +: user is voiced",
      NULL
};

static char *who_user_help[] = {
      "/WHO [+|-][achmnsuzCM] [args]",
      "Flags are specified like channel modes,",
      "The flags cghimnsuz 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 o:             user is oper",
      " Flag c <channel>:   user is on <channel>,",
      "                     no wildcards accepted",
      " Flag h <host>:      user has string <host> in their hostname,",
      "                     wildcards accepted",
      " Flag m <usermodes>: user has <usermodes> set on them,",
      "                     limited to o/A/N/a",
      " Flag n <nick>:      user has string <nick> in their nickname,",
      "                     wildcards accepted",
      " Flag s <server>:    user is on server <server>,",
      "                     wildcards not accepted",
      " Flag u <user>:      user has string <user> in their username,",
      "                     wildcards accepted",
      " ",
      "Behavior flags:",
      " Flag C: show first visible channel user is in",
      " Flag M: check for user in channels I am a member of",
      " ",
      "Returned flags:",
      " Flag H: user is here (not away)",
      " Flag G: user is gone (away)",
      " Flag r: users' nickname is registered",
      " Flag *: user is a server operator",
      " Flag @: user is a channel operator",
      " Flag %: user is a half operator",
      " Flag +: user is voiced",
      NULL
};

static void show_help(aClient *sptr)
{
      char **s = (HasMode(sptr, UMODE_OPER)) ? who_oper_help : who_user_help;
      while (*s != NULL) {
            send_me_numeric(sptr, RPL_COMMANDSYNTAX, *s);
            s++;
      }
      send_me_numeric(sptr, RPL_ENDOFWHO, "?");
}

static int syntax_error(aClient *sptr)
{
      send_me_numericNA(sptr, ERR_WHOSYNTAX);
      return 0;
}

#define WF_CHECK_AWAY   0x0001
#define WF_CHECK_OPER   0x0002
#define WF_CHECK_UMODES 0x0004
#define WF_SHOW_CHAN    0x0008
#define WF_COMMON_CHAN  0x0010
#define WF_REAL_HOSTS   0x0020
#define WF_REAL_IPADDRS 0x0040

typedef struct _who_opts who_opts;
struct _who_opts {
      unsigned w_away : 1;
      unsigned w_oper : 1;
      unsigned w_nick : 1;
      unsigned w_user : 1;
      unsigned w_host : 1;
      unsigned w_maskedhost : 1;
      unsigned w_ipaddr : 1;
      unsigned w_gcos : 1;
      unsigned w_chan : 1;
      unsigned w_serv : 1;
      unsigned w_umodes : 1;

      char *nick;
      char *user;
      char *host;
      char *maskedhost;
      char *ipaddr;
      char *gcos;
      unsigned long chan_flags;
      aChannel *chan;
      aClient *serv;
      unsigned long umodes;

      unsigned short flags;
};

static who_opts Who;

/* These are ugly, but they make life easier. They are here because most of them are
 * repeated at various places, and using the code direct makes it pretty unreadable.
 */

#define ADD_ONLY        if (!change) return syntax_error(sptr)

#define OPER_ONLY       if (!HasMode(sptr, UMODE_OPER)) return syntax_error(sptr)

#define GET_ARG               if (parv[args] == NULL) return syntax_error(sptr); arg = parv[args++]

#define PUT_ARG(xx, yy)       GET_ARG; (xx) = arg; (yy) = change

#define SET_FLAG(xx)          if (change) Who.flags |= (xx); else Who.flags &= ~(xx)

#define VALID_CHAN_OPTS       ((Who.flags & (WF_CHECK_AWAY|WF_CHECK_OPER)) || Who.w_gcos || Who.w_host \
                        || (Who.flags & WF_CHECK_UMODES) || Who.w_serv || Who.w_nick \
                        || Who.w_user || Who.w_ipaddr)

#define WHO_HOST(xx)          (Who.flags & WF_REAL_IPADDRS) ? xx->hostip : \
                              (Who.flags & WF_REAL_HOSTS) ? xx->host : MaskedHost(xx)

#define WHO_HOPCOUNT(xx, yy)  (IsULine(xx) && !HasMode(yy, UMODE_OPER)) ? 0 : xx->hopcount

#define WHO_END               (Who.host != NULL ? Who.host : Who.nick != NULL ? Who.nick : \
                              Who.user != NULL ? Who.user : Who.gcos != NULL ? Who.gcos : \
                              Who.serv != NULL ? Who.serv->name : "*")

static unsigned long get_who_chan_flags(char **arg)
{
      unsigned long flags = 0;
      char *s;

      for (s = *arg; *s != '\0'; s++) {
            if (*s == '*') {
                  flags |= CMODE_CHANADMIN;
                  continue;
            }
            if (*s == '@') {
                  flags |= CMODE_CHANOP;
                  continue;
            }
            if (*s == '%') {
                  flags |= CMODE_HALFOP;
                  continue;
            }
            if (*s == '+') {
                  flags |= CMODE_VOICE;
                  continue;
            }
            break;
      }

      if (*s != '#' && (*s != '&')) {
            return 0;
      }

      *arg = s;
      return flags;
}

static unsigned long get_who_usermodes(char *arg)
{
      unsigned long modes = 0;
      int mindex;

      while (*arg != '\0') {
            if ((mindex = usermodes->map[(unsigned char)*arg]) >= 0) {
                  modes |= usermodes->table[mindex].mode;
            }
            arg++;
      }

      return modes;
}

static int parse_who_options(aClient *sptr, int parc, char *parv[])
{
      char *p = parv[0], *arg;
      short change = 1;
      int args = 1;

      memset((char *)&Who, '\0', sizeof(Who));

      if (parc < 1 || *parv[0] == '?') {
            show_help(sptr);
            return 0;
      }

      if (*p == '0' && *(p + 1) == '\0') {
            if (parc > 1 && (*parv[1] == 'o')) {
                  Who.flags |= WF_CHECK_UMODES;
                  Who.umodes = UMODE_OPER;
                  Who.w_umodes = change;
            }

            Who.w_host = change;
            Who.host = "*";
            return 1;
      }

      if (*p != '+' && *p != '-') {
            if (*p == '#' || *p == '&') {
                  if ((Who.chan = find_channel(p, NULL)) == NULL) {
                        send_me_numeric(sptr, ERR_NOSUCHCHANNEL, p);
                        return 0;
                  }
            }
            else if (strchr(p, '.') != NULL) {
                  Who.host = p;
                  Who.w_host = 1;
            }
            else {
                  Who.nick = p;
                  Who.w_nick = 1;
            }
            return 1;
      }

      while (*p != '\0') {
            switch (*p) {
                  case '+':
                        change = 1;
                        break;
                  case '-':
                        change = 0;
                        break;
                  case 'a':
                        SET_FLAG(WF_CHECK_AWAY);
                        Who.w_away = change;
                        break;
                  case 'o':
                        SET_FLAG(WF_CHECK_OPER);
                        Who.w_oper = change;
                        break;
                  case 'C':
                        SET_FLAG(WF_SHOW_CHAN);
                        break;
                  case 'M':
                        SET_FLAG(WF_COMMON_CHAN);
                        break;
                  case 'c':
                        ADD_ONLY;
                        GET_ARG;

                        if (*arg != '#' && (*arg != '&')) {
                              Who.chan_flags = get_who_chan_flags(&arg);
                        }
                        else {
                              Who.chan_flags = 0;
                        }
                        if ((Who.chan = find_channel(arg, NULL)) == NULL) {
                              send_me_numeric(sptr, ERR_NOSUCHCHANNEL, arg);
                              return 0;
                        }
                        Who.w_chan = change;
                        break;
                  case 'g':
                        OPER_ONLY;
                        PUT_ARG(Who.gcos, Who.w_gcos);
                        break;
                  case 'H':
                        OPER_ONLY;
                        SET_FLAG(WF_REAL_HOSTS);
                        break;
                  case 'h':
                        PUT_ARG(Who.host, Who.w_host);
                        break;
                  case 'I':
                        OPER_ONLY;
                        SET_FLAG(WF_REAL_IPADDRS);
                        break;
                  case 'i':
                        OPER_ONLY;
                        PUT_ARG(Who.ipaddr, Who.w_ipaddr);
                        break;
                  case 'm':
                        GET_ARG;

                        Who.umodes = get_who_usermodes(arg);
                        if (!HasMode(sptr, UMODE_OPER)) {
                              Who.umodes = (Who.umodes & (UMODE_OPER|UMODE_ADMIN|UMODE_NETADMIN|UMODE_SADMIN));
                        }
                        if (Who.umodes) {
                              Who.flags |= WF_CHECK_UMODES;
                        }
                        Who.w_umodes = change;
                        break;
                  case 'n':
                        PUT_ARG(Who.nick, Who.w_nick);
                        break;
                  case 's':
                        ADD_ONLY;
                        GET_ARG;

                        if ((Who.serv = find_server(arg, NULL)) == NULL) {
                              send_me_numeric(sptr, ERR_NOSUCHSERVER, arg);
                              return 0;
                        }
                        Who.w_serv = change;
                        break;
                  case 'u':
                        PUT_ARG(Who.user, Who.w_user);
                        break;
                  default:
                        return syntax_error(sptr);
            }
            p++;
      }

      if ((Who.flags & WF_COMMON_CHAN) && !VALID_CHAN_OPTS) {
            GET_ARG;

            if (Who.chan != NULL || Who.nick != NULL || *arg != '#' || *arg != '&'
              || (strchr(arg, '.') == NULL)) {
                  return syntax_error(sptr);
            }

            Who.host = arg;
            Who.w_host = 1;
      }
      else if ((Who.flags & WF_SHOW_CHAN) && !VALID_CHAN_OPTS) {
            GET_ARG;

            if (strchr(arg, '.') != NULL) {
                  Who.host = arg;
                  Who.w_host = 1;
            }
            else {
                  Who.nick = arg;
                  Who.w_nick = 1;
            }
      }
      return 1;
}

static inline int who_cmp(char *str1, char *str2)
{
      if (has_wilds(str1)) {
            return match(str1, str2);
      }
      return mycmp(str1, str2);
}

static void build_user_status(aClient *acptr, chanMember *cm, char *buf, aClient *sptr)
{
      char *p = buf;

      if (acptr->user->away == NULL) {
            *p++ = 'H';
      }
      else {
            *p++ = 'G';
      }
      if (HasMode(acptr, UMODE_REGNICK)) {
            *p++ = 'r';
      }
      if (HasMode(acptr, UMODE_SECURE)) {
            *p++ = 'z';
      }

      if (HasMode(acptr, UMODE_OPER)) {
            *p++ = '*';
      }
      else if (HasMode(acptr, UMODE_INVISIBLE) && HasMode(sptr, UMODE_OPER)) {
            *p++ = '!';
      }

      if (cm != NULL) {
            if (cm->flags & CMODE_CHANOP) {
                  *p++ = '@';
            }
            else if (cm->flags & CMODE_HALFOP) {
                  *p++ = '%';
            }
            else if (cm->flags & CMODE_VOICE) {
                  *p++ = '+';
            }
      }

      *p = '\0';
}

static char *first_visible_channel(aClient *acptr, aClient *sptr)
{
      aChannel *chptr = NULL;
      int secret = 0;

      if (acptr->user->channel == NULL) {
            return "*";
      }

      if (HasMode(sptr, UMODE_SADMIN)) {
            chptr = acptr->user->channel->chptr;
            if (!ShowChannel(sptr, chptr)) {
                  secret = 1;
            }
      }
      else {
            chanMember *cm;
            for (cm = acptr->user->channel; cm != NULL; cm = cm->nextchan) {
                  if (ShowChannel(sptr, cm->chptr)) {
                        chptr = cm->chptr;
                        break;
                  }
            }
      }

      if (chptr != NULL) {
            if (secret) {
                  static char buf[CHANNELLEN + 2];
                  ircsprintf(buf, "!%s", chptr->chname);
                  return buf;
            }
            return chptr->chname;
      }

      return "*";
}

static void show_who_reply(aClient *sptr, aClient *acptr, char *status, aChannel *chptr, int first_visible)
{
      char *name;

      if (first_visible) {
            name = (Who.flags & WF_SHOW_CHAN) ? first_visible_channel(acptr, sptr) : "*";
      }
      else {
            name = (chptr != NULL) ? chptr->chname : acptr->name;
      }
      send_me_numeric(sptr, RPL_WHOREPLY, name, acptr->username, WHO_HOST(acptr), acptr->user->server,
            acptr->name, status, WHO_HOPCOUNT(acptr, sptr), acptr->info);
}

static int can_see(aClient *acptr, int showall, aClient *sptr)
{
      if (!IsClient(acptr) || (HasMode(acptr, UMODE_INVISIBLE) && !showall)) {
            return 0;
      }

      if (Who.flags & WF_CHECK_UMODES) {
            if (Who.w_umodes && ((acptr->umode & Who.umodes) != Who.umodes)) {
                  return 0;
            }
            if (!Who.w_umodes && ((acptr->umode & Who.umodes) == Who.umodes)) {
                  return 0;
            }
      }

      if (Who.flags & WF_CHECK_AWAY) {
            if (Who.w_away && BadPtr(acptr->user->away)) {
                  return 0;
            }
            if (!Who.w_away && !BadPtr(acptr->user->away)) {
                  return 0;
            }
      }

      if (Who.flags & WF_CHECK_OPER) {
            if (Who.w_oper && !HasMode(acptr, UMODE_OPER)) {
                  return 0;
            }
            if (!Who.w_oper && HasMode(acptr, UMODE_OPER)) {
                  return 0;
            }
      }

      if (Who.w_serv && (Who.serv != acptr->uplink)) {
            return 0;
      }

      if (Who.user != NULL) {
            int diff = who_cmp(Who.user, acptr->username);
            if ((Who.w_user && diff) || (!Who.w_user && !diff)) {
                  return 0;
            }
      }
      if (Who.nick != NULL) {
            int diff = who_cmp(Who.nick, acptr->name);
            if ((Who.w_nick && diff) || (!Who.w_nick && !diff)) {
                  return 0;
            }
      }
      if (Who.host != NULL) {
            char *host = (HasMode(sptr, UMODE_OPER)) ? acptr->host : MaskedHost(acptr);
            int diff = who_cmp(Who.host, host);
            if ((Who.w_host && diff) || (!Who.w_host && !diff)) {
                  return 0;
            }
      }
      if (Who.w_ipaddr) {
            if (who_cmp(Who.ipaddr, acptr->hostip)) {
                  return 0;
            }
      }
      if (Who.gcos != NULL) {
            int diff = who_cmp(Who.gcos, acptr->info);
            if ((Who.w_gcos && diff) || (!Who.w_gcos && !diff)) {
                  return 0;
            }
      }

      return 1;
}

static int do_who_channel(aClient *sptr, aChannel *chptr)
{
      int showall = 0;
      chanMember *cm;
      aClient *acptr;
      char status[8];

      if (IsMember(sptr, chptr)) {
            showall = 1;
      }
      else if (SecretChannel(chptr) && HasMode(sptr, UMODE_SADMIN)) {
            showall = 1;
      }
      else if (!SecretChannel(chptr) && HasMode(sptr, UMODE_OPER)) {
            showall = 1;
      }

      if (!showall && SecretChannel(chptr)) {
            send_me_numeric(sptr, RPL_ENDOFWHO, chptr->chname);
            return 0;
      }

      for (cm = chptr->members; cm != NULL; cm = cm->nextuser) {
            acptr = cm->cptr;

            if (!can_see(acptr, showall, sptr)) {
                  continue;
            }
            if (Who.chan_flags && !(cm->flags & Who.chan_flags)) {
                  continue;
            }

            build_user_status(acptr, cm, status, sptr);
            show_who_reply(sptr, acptr, status, chptr, 0);
      }

      send_me_numeric(sptr, RPL_ENDOFWHO, chptr->chname);
      return 0;
}

/*
 * m_who
 *    parv[0] = sender prefix
 *    parv[1] = options
 */
int m_who(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
      aClient *acptr;
      chanMember *chm, *cm;
      int i = 0;
      char status[8];

      if (!MyClient(sptr)) {
            return 0;
      }
      if (!parse_who_options(sptr, parc - 1, parv + 1)) {
            return 0;
      }

      if (Who.chan != NULL) {
            return do_who_channel(sptr, Who.chan);
      }

      if ((Who.nick != NULL) && !has_wilds(Who.nick)) {
            if ((acptr = find_person(Who.nick, NULL)) != NULL) {
                  if (can_see(acptr, 1, sptr)) {
                        i = 1;
                  }
            }
            if (i) {
                  build_user_status(acptr, NULL, status, sptr);
                  show_who_reply(sptr, acptr, status, NULL, 1);
            }
            send_me_numeric(sptr, RPL_ENDOFWHO, (Who.host != NULL) ? Who.host : Who.nick);
            return 0;
      }

      if (!(Who.flags & WF_COMMON_CHAN)) {
            for (acptr = client; acptr != NULL; acptr = acptr->next) {
                  if (!can_see(acptr, (HasMode(sptr, UMODE_OPER) ? 1 : 0), sptr)) {
                        continue;
                  }
                  if ((GeneralConfig.max_who_replies > 0) && (i >= GeneralConfig.max_who_replies)
                    && !HasMode(sptr, UMODE_OPER)) {
                        send_me_numeric(sptr, ERR_WHOLIMEXCEED, GeneralConfig.max_who_replies);
                        break;
                  }

                  build_user_status(acptr, NULL, status, sptr);
                  show_who_reply(sptr, acptr, status, NULL, 1);
                  i++;
            }
            send_me_numeric(sptr, RPL_ENDOFWHO, WHO_END);
            return 0;
      }

      for (chm = sptr->user->channel; chm != NULL; chm = chm->nextchan) {
            i = 0;
            for (cm = chm->chptr->members; cm != NULL; cm = cm->nextuser) {
                  acptr = cm->cptr;

                  if (!can_see(acptr, 1, sptr)) {
                        continue;
                  }
                  if ((GeneralConfig.max_who_replies > 0) && (i >= GeneralConfig.max_who_replies)
                    && !HasMode(sptr, UMODE_OPER)) {
                        send_me_numeric(sptr, ERR_WHOLIMEXCEED, GeneralConfig.max_who_replies);
                        break;
                  }

                  build_user_status(acptr, cm, status, sptr);
                  show_who_reply(sptr, acptr, status, chm->chptr, 0);
                  i++;
            }
      }

      send_me_numeric(sptr, RPL_ENDOFWHO, WHO_END);
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index