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

channel_mode.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: channel_mode.c,v 1.67.2.1 2004/12/07 03:05:07 pneumatus Exp $
 */

#undef CMDEBUG

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "channel.h"
#include "h.h"
#include "memory.h"
#include "xmode.h"

char modebuf[REALMODEBUFLEN]; /* mode buffer */
char parabuf[REALMODEBUFLEN]; /* para buffer */
char idparabuf[REALMODEBUFLEN];     /* SUID-para buffer */
int midx, pidx, idpidx;       /* mode, para, SUID-para buffer indexes */

int chanmode_prefix_len = 0;  /* :cptr->name MODE chptr->chname */
char *pptr;             /* temporary string pointer */

xModeTable *chanmodes = NULL;
chanMode *chanmode_tab[XMODE_TABLE_SIZE + 1];

char chanmode_str[(XMODE_TABLE_SIZE * 2) + 1];  /* all modes, param modes, NULL */
char chanmode_isupport[XMODE_TABLE_SIZE + 4];   /* 3 commas, NULL */

XMODE(CMODE_BAN);
XMODE(CMODE_INVITEONLY);
XMODE(CMODE_KEY);
XMODE(CMODE_LIMIT);
XMODE(CMODE_MODERATED);
XMODE(CMODE_NOPRIVMSGS);
XMODE(CMODE_CHANOP);
XMODE(CMODE_PRIVATE);
XMODE(CMODE_REGISTERED);
XMODE(CMODE_REGONLY);
XMODE(CMODE_SECRET);
XMODE(CMODE_TOPICLIMIT);
XMODE(CMODE_VOICE);
XMODE(CMODE_NOCOLOUR);
XMODE(CMODE_OPERONLY);
XMODE(CMODE_ADMINONLY);
XMODE(CMODE_HALFOP);
XMODE(CMODE_CHANADMIN);
XMODE(CMODE_NOCTCP);
XMODE(CMODE_EXCEPT);
XMODE(CMODE_NONICKCH);
XMODE(CMODE_INVEX);
XMODE(CMODE_MODREGONLY);

/* do_mode, get_mode, sjoin_domode_pbuf, sjoin_cmp_para, sjoin_synch_para */

#define STANDARD  do_mode_standard, get_mode_standard, NULL, NULL, NULL
#define AOHV            do_mode_aohv, NULL, NULL, NULL, NULL
#define BEI       do_mode_beI, NULL, NULL, NULL, NULL
#define MODEK           do_mode_k, get_mode_k, sj_domode_pbuf_k, sj_cmp_para_k, sj_synch_para_k
#define MODEL           do_mode_l, get_mode_l, sj_domode_pbuf_l, sj_cmp_para_l, sj_synch_para_l

chanMode CM_BAN         = { BEI,    CPRIV_CHANOP,     CPARA_SETUNSET,   CPTYP_ADDRLIST };
chanMode CM_INVITEONLY  = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_KEY         = { MODEK,  CPRIV_CHANOP,     CPARA_SETUNSET,   CPTYP_NORMAL };
chanMode CM_LIMIT = { MODEL,  CPRIV_CHANOP,     CPARA_SETONLY,    CPTYP_NORMAL };
chanMode CM_MODERATED   = { STANDARD,     CPRIV_CHANOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_NOPRIVMSGS  = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_CHANOP      = { AOHV,   CPRIV_CHANOP,     CPARA_SETUNSET, CPTYP_USERPRIV };
chanMode CM_PRIVATE     = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_REGISTERED  = { STANDARD,     CPRIV_ULINE,      CPARA_NONE, CPTYP_NONE };
chanMode CM_REGONLY     = { STANDARD,     CPRIV_CHANOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_SECRET      = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_TOPICLIMIT  = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_VOICE = { AOHV,   CPRIV_HALFOP,     CPARA_SETUNSET,   CPTYP_USERPRIV };
chanMode CM_NOCOLOUR    = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_OPERONLY    = { STANDARD,     CPRIV_CHANOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_ADMINONLY   = { STANDARD,     CPRIV_CHANOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_HALFOP      = { AOHV,   CPRIV_CHANOP,     CPARA_SETUNSET,   CPTYP_USERPRIV };
chanMode CM_CHANADMIN   = { AOHV,   CPRIV_CHANADMIN,CPARA_SETUNSET,     CPTYP_NICKLIST };
chanMode CM_EXCEPT      = { BEI,    CPRIV_CHANOP,     CPARA_SETUNSET,   CPTYP_ADDRLIST };
chanMode CM_NOCTCP      = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_NONICKCH    = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };
chanMode CM_INVEX = { BEI,    CPRIV_CHANOP,     CPARA_SETUNSET,   CPTYP_ADDRLIST };
chanMode CM_MODREGONLY  = { STANDARD,     CPRIV_HALFOP,     CPARA_NONE, CPTYP_NONE };

static void update_chanmode_str()
{
      char *p = chanmode_str;
      int i;

      for (i = 0; i <= chanmodes->highest; i++) {
            if (chanmodes->table[i].flag != '\0') {
                  *p++ = chanmodes->table[i].flag;
            }
      }
      *p++ = ' ';

      for (i = 0; i <= chanmodes->highest; i++) {
            if ((chanmodes->table[i].flag != '\0') && (chanmode_tab[i]->paratype >= CPTYP_NORMAL)) {
                  *p++ = chanmodes->table[i].flag;
            }
      }
      *p++ = '\0';
}

static void update_chanmode_isupport()
{
      char nopara[32], setonly[32], setunset[32], list[32];
      int nopara_i = 0, setonly_i = 0, setunset_i = 0, list_i = 0, i;

      for (i = 0; i <= chanmodes->highest; i++) {
            if (chanmodes->table[i].flag == '\0') {
                  continue;
            }
            if (chanmode_tab[i]->para == CPARA_NONE) {
                  ADD_CHAR(chanmodes->table[i].flag, nopara, nopara_i);
                  continue;
            }

            ASSERT(chanmode_tab[i]->paratype >= CPTYP_NORMAL);

            if (chanmode_tab[i]->para == CPARA_SETONLY) {
                  ADD_CHAR(chanmodes->table[i].flag, setonly, setonly_i);
                  continue;
            }

            /* CPARA_SETUNSET kludge */
            ASSERT(chanmode_tab[i]->para > CPARA_NONE);

            if ((chanmode_tab[i]->paratype == CPTYP_ADDRLIST)
              || (chanmode_tab[i]->paratype == CPTYP_NICKLIST)) {
                  ADD_CHAR(chanmodes->table[i].flag, list, list_i);
            }
            else if (chanmode_tab[i]->paratype != CPTYP_USERPRIV) {
                  ADD_CHAR(chanmodes->table[i].flag, setunset, setunset_i);
            }
      }

      ircsprintf(chanmode_isupport, "%s,%s,%s,%s", list, setunset, setonly, nopara);
      build_isupport();
}

int add_chan_mode(char flag, long *mode, chanMode *m)
{
      int i;

      if ((i = add_xmode(chanmodes, (unsigned char)flag, mode)) < 0) {
            ircdlog(LOG_ERROR, "Failed to create chan mode '%c' (%s)", flag, xmode_errstr(i));
            sendto_realops("Failed to create chan mode '%c' (%s)", flag, xmode_errstr(i));
            return -1;
      }

      chanmode_tab[i] = m;
      update_chanmode_str();
      update_chanmode_isupport();

      return i;
}

int del_chan_mode(char flag, long *mode)
{
      return 0;
}

void init_chan_mode()
{
      chanmodes = init_xmode_table();
      memset(chanmode_tab, '\0', sizeof(chanmode_tab));

      add_chan_mode('b', &CMODE_BAN, &CM_BAN);
      add_chan_mode('i', &CMODE_INVITEONLY, &CM_INVITEONLY);
      add_chan_mode('k', &CMODE_KEY, &CM_KEY);
      add_chan_mode('l', &CMODE_LIMIT, &CM_LIMIT);
      add_chan_mode('m', &CMODE_MODERATED, &CM_MODERATED);
      add_chan_mode('n', &CMODE_NOPRIVMSGS, &CM_NOPRIVMSGS);
      add_chan_mode('o', &CMODE_CHANOP, &CM_CHANOP);
      add_chan_mode('p', &CMODE_PRIVATE, &CM_PRIVATE);
      add_chan_mode('r', &CMODE_REGISTERED, &CM_REGISTERED);
      add_chan_mode('R', &CMODE_REGONLY, &CM_REGONLY);
      add_chan_mode('s', &CMODE_SECRET, &CM_SECRET);
      add_chan_mode('t', &CMODE_TOPICLIMIT, &CM_TOPICLIMIT);
      add_chan_mode('v', &CMODE_VOICE, &CM_VOICE);
      add_chan_mode('c', &CMODE_NOCOLOUR, &CM_NOCOLOUR);
      add_chan_mode('O', &CMODE_OPERONLY, &CM_OPERONLY);
      add_chan_mode('A', &CMODE_ADMINONLY, &CM_ADMINONLY);
      add_chan_mode('h', &CMODE_HALFOP, &CM_HALFOP);
      add_chan_mode('a', &CMODE_CHANADMIN, &CM_CHANADMIN);
      add_chan_mode('e', &CMODE_EXCEPT, &CM_EXCEPT);
      add_chan_mode('C', &CMODE_NOCTCP, &CM_NOCTCP);
      add_chan_mode('N', &CMODE_NONICKCH, &CM_NONICKCH);
      add_chan_mode('I', &CMODE_INVEX, &CM_INVEX);
      add_chan_mode('M', &CMODE_MODREGONLY, &CM_MODREGONLY);
}

void get_chan_modes(aClient *sptr, aChannel *chptr, char *mode_buf, char *para_buf)
{
      int i, m_idx = 0, p_idx = 0;

      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      ADD_CHAR('+', mode_buf, m_idx);
      para_buf[p_idx] = '\0';

      for (i = 0; i <= chanmodes->highest; i++) {
            if ((chanmodes->table[i].flag == '\0') || (chanmode_tab[i]->get_mode == NULL)) {
                  continue;
            }
            (*chanmode_tab[i]->get_mode)(sptr, chptr, i, mode_buf, &m_idx, para_buf, &p_idx);
      }
}

int has_mode(aClient *sptr, aChannel *chptr, long mode)
{
      chanMember *cm;

      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);
      ASSERT(mode > 0);

      if (IsPerson(sptr)) {
            if ((cm = find_user_member(sptr->user->channel, chptr)) != NULL) {
                  return (cm->flags & mode) ? 1 : 0;
            }
      }

      return 0;
}

void get_mode_standard(aClient *sptr, aChannel *chptr, int mindex, char *mode_buf, int *m_idx,
  char *para_buf, int *p_idx)
{
      xMode *t = &chanmodes->table[mindex];

      ASSERT(chptr != NULL);

      if (chptr->mode.mode & t->mode) {
            ADD_CHAR(t->flag, mode_buf, (*m_idx));
      }
}

int do_mode_standard(aClient *cptr, aClient *sptr, aChannel *chptr, int mindex, int level,
  short change, char *para, short *errors, unsigned int *changes)
{
      xMode *t = &chanmodes->table[mindex];

      ASSERT(cptr != NULL);
      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      if (MyClient(sptr)) {
            if (t->mode == CMODE_ADMINONLY) {
                  if (!HasMode(sptr, UMODE_NETADMIN|UMODE_ADMIN)) {
                        (*errors) |= CM_ERR_NOTADMIN;
                        return CM_STAT_FAILURE;
                  }
                  if (chptr->mode.mode & CMODE_OPERONLY) {
                        return CM_STAT_BOUNCE;
                  }
            }
            if (t->mode == CMODE_OPERONLY) {
                  if (!HasMode(sptr, UMODE_OPER)) {
                        (*errors) |= CM_ERR_NOTIRCOP;
                        return CM_STAT_FAILURE;
                  }
                  if (chptr->mode.mode & CMODE_ADMINONLY) {
                        return CM_STAT_BOUNCE;
                  }
            }
      }

      if (change == XMODE_ADD) {
            chptr->mode.mode |= t->mode;
      }
      else {
            chptr->mode.mode &= ~t->mode;
      }

      ADD_MODE(t->flag);
      return CM_STAT_SUCCESS;
}

int do_mode_beI(aClient *cptr, aClient *sptr, aChannel *chptr, int mindex, int level,
  short change, char *para, short *errors, unsigned int *changes)
{
      xMode *t = &chanmodes->table[mindex];
      char nuhbuf[NICKLEN + USERLEN + HOSTLEN + 6];

      ASSERT(cptr != NULL);
      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      if (para == NULL) {
            ASSERT(!IsServer(sptr));

            if (t->mode == CMODE_BAN) {
                  send_ban_list(sptr, chptr, &chptr->banlist, RPL_BANLIST, RPL_ENDOFBANLIST);
            }
            if (t->mode == CMODE_EXCEPT) {
                  send_ban_list(sptr, chptr, &chptr->exceptlist, RPL_EXCEPTLIST, RPL_ENDOFEXCEPTLIST);
            }
            if (t->mode == CMODE_INVEX) {
                  send_ban_list(sptr, chptr, &chptr->invexlist, RPL_INVEXLIST, RPL_ENDOFINVEXLIST);
            }
            return CM_STAT_BOUNCE;
      }

      if (BadPtr(para) || *para == ':') {
            return CM_STAT_FAILURE;
      }

      strcpy(nuhbuf, collapse(pretty_mask(para)));
      para = nuhbuf;

      if (OVERFLOW(strlen(para))) {
            return CM_STAT_FAILURE;
      }

      if (change == XMODE_ADD && !add_id(para, chptr, t->mode, sptr)) {
            return CM_STAT_FAILURE;
      }
      if (change == XMODE_DEL && !del_id(para, chptr, t->mode)) {
            return CM_STAT_FAILURE;
      }

      ADD_MODE(t->flag);
      ADD_PARA(para);
      return CM_STAT_SUCCESS;
}

int do_mode_aohv(aClient *cptr, aClient *sptr, aChannel *chptr, int mindex, int level,
  short change, char *para, short *errors, unsigned int *changes)
{
      xMode *t = &chanmodes->table[mindex];
      aClient *acptr = NULL;
      chanMember *cm = NULL;

      ASSERT(cptr != NULL);
      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      if (OVERFLOW(NICKLEN)) {
            return CM_STAT_FAILURE;
      }

      acptr = IsServer(cptr) ? find_target_chasing(sptr, para, MSG_MODE) : find_chasing(sptr, para, NULL, MSG_MODE);
      if (acptr == NULL || (acptr->user == NULL)) {
            cm = NULL;
      }
      else {
            cm = find_user_member(acptr->user->channel, chptr);
      }
      if (cm == NULL) {
            return CM_STAT_FAILURE;
      }

      if (level < CPRIV_ULINE) {
            if (is_chanadmin(acptr, chptr) && (change == XMODE_DEL) && !HasMode(sptr, UMODE_NETADMIN)
              && (acptr != sptr)) {
                  (*errors) |= CM_ERR_NOTNETADMIN;
                  return CM_STAT_FAILURE;
            }
      }

      if (change == XMODE_ADD) {
            if (cm->flags & t->mode) {
                  return CM_STAT_BOUNCE;
            }
            cm->flags |= t->mode;
      }
      else {
            if (!(cm->flags & t->mode)) {
                  return CM_STAT_BOUNCE;
            }
            cm->flags &= ~t->mode;
      }

      ADD_MODE(t->flag);
      ADD_NPARA(acptr->name);
      ADD_IPARA(get_id(acptr));

      return CM_STAT_SUCCESS;
}

void get_mode_k(aClient *sptr, aChannel *chptr, int mindex, char *mode_buf, int *m_idx,
  char *para_buf, int *p_idx)
{
      xMode *t = &chanmodes->table[mindex];

      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      if (BadPtr(chptr->mode.key)) {
            return;
      }

      ADD_CHAR(t->flag, mode_buf, (*m_idx));
      if (!MyClient(sptr) || IsMember(sptr, chptr)) {
            ADD_STRING(chptr->mode.key, para_buf, (*p_idx));
      }
}

int do_mode_k(aClient *cptr, aClient *sptr, aChannel *chptr, int mindex, int level,
  short change, char *para, short *errors, unsigned int *changes)
{
      xMode *t = &chanmodes->table[mindex];

      ASSERT(cptr != NULL);
      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      if ((*changes) & CMODE_KEY) {
            return CM_STAT_BOUNCE;
      }

      if (change == XMODE_DEL && BadPtr(chptr->mode.key)) {
            return CM_STAT_BOUNCE;
      }
      if (BadPtr(para) || *para == ':') {
            return CM_STAT_FAILURE;
      }
      if (strchr(para, '*') || strchr(para, ' ') || strchr(para, ',')) {
            return CM_STAT_FAILURE;
      }
      if (OVERFLOW(KEYLEN + 2)) {
            return CM_STAT_FAILURE;
      }

      ADD_MODE(t->flag);
      if (change == XMODE_ADD) {
            strncpy(chptr->mode.key, para, KEYLEN);
            ADD_PARA(chptr->mode.key);
      }
      else {
            ADD_PARA(chptr->mode.key);
            *chptr->mode.key = '\0';
      }

      (*changes) |= CMODE_KEY;
      return CM_STAT_SUCCESS;
}

void get_mode_l(aClient *sptr, aChannel *chptr, int mindex, char *mode_buf, int *m_idx,
  char *para_buf, int *p_idx)
{
      xMode *t = &chanmodes->table[mindex];

      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      if (!chptr->mode.limit) {
            return;
      }

      ADD_CHAR(t->flag, mode_buf, (*m_idx));
      if (!MyClient(sptr) || IsMember(sptr, chptr)) {
            ADD_STRING(my_itoa(chptr->mode.limit), para_buf, (*p_idx));
      }
}

int do_mode_l(aClient *cptr, aClient *sptr, aChannel *chptr, int mindex, int level,
  short change, char *para, short *errors, unsigned int *changes)
{
      xMode *t = &chanmodes->table[mindex];

      ASSERT(cptr != NULL);
      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

      if ((*changes) & CMODE_LIMIT) {
            return CM_STAT_BOUNCE;
      }

      if (change == XMODE_ADD) {
            char tmp[16];
            int i;

            if (OVERFLOW(strlen(para))) {
                  return CM_STAT_FAILURE;
            }
            if ((i = atoi(para)) < 1) {
                  return CM_STAT_FAILURE;
            }

            ircsprintf(tmp, "%d", i);
            chptr->mode.limit = i;
            chptr->mode.mode |= t->mode;

            ADD_MODE(t->flag);
            ADD_PARA(tmp);
      }
      else {
            if (!(chptr->mode.mode & t->mode)) {
                  return CM_STAT_BOUNCE;
            }
            chptr->mode.mode &= ~t->mode;
            chptr->mode.limit = 0;

            ADD_MODE(t->flag);
      }

      (*changes) |= CMODE_LIMIT;
      return CM_STAT_SUCCESS;
}

int parse_mode(aClient *cptr, aClient *sptr, aChannel *chptr, int level, int parc, char *parv[])
{
      int args = 1;                 /* what argument we're on */
      short change = XMODE_ADD;     /* by default we add modes */
      short errors = 0;       /* errors */
      unsigned int changes = 0;     /* changes occured so far */

      int mindex = -1;        /* index into xmode table for mode flag searching */
      chanMode *m = NULL;           /* pointer to chanmode_tab entry */
      xMode *t = NULL;        /* pointer to chanmodes->table entry */

      char *modes;                  /* users idea of mode change */
      int nmodes = 0;               /* how many modes we set */
      int nparams = 0;        /* how many params we set */
      int maxparams = MAXMODEPARAMSUSER; /* max parameters to allow */
      char *mode_para;        /* mode parameter */

      /* ********************************************************************************* */

      ASSERT(cptr != NULL);
      ASSERT(sptr != NULL);
      ASSERT(chptr != NULL);

#ifdef CMDEBUG
      Debug((DEBUG_DEBUG, "parse_mode() from cptr %x (%s) sptr %x (%s)", cptr, cptr->name,
            sptr, sptr->name));
#endif

      ASSERT(parc >= 1);

      /* pre-calculate the buffer length up to PBUF */
      chanmode_prefix_len = HasSUID(cptr) ? IRCD_MAX(strlen(cptr->name), 8) : strlen(cptr->name);
      chanmode_prefix_len += strlen(chptr->chname) + 32;

      midx = pidx = idpidx = 0;

      ADD_MODE('+');
      parabuf[pidx] = idparabuf[idpidx] = '\0';

#ifdef CMDEBUG
      Debug((DEBUG_DEBUG, "parse_mode(%s) parsing [%s]", chptr->chname, parv[0]));
#endif

      if (IsServer(sptr) || IsULine(sptr)) {
            maxparams = 512;
      }
      for (modes = parv[0]; (*modes != '\0'); modes++) {
            if (*modes == '+') {
                  if (change == XMODE_ADD) {
                        continue; /* already + */
                  }

                  change = XMODE_ADD;
                  if (modebuf[midx - 1] == '-') {
                        modebuf[midx - 1] = '+';
                        continue; /* skip -+ strings (repeating) */
                  }

                  ADD_MODE('+');
                  continue;
            }
            if (*modes == '-') {
                  if (change == XMODE_DEL) {
                        continue; /* already - */
                  }

                  change = XMODE_DEL;
                  if (modebuf[midx - 1] == '+') {
                        modebuf[midx - 1] = '-';
                        continue; /* skip +- strings (repeating) */
                  }

                  ADD_MODE('-');
                  continue;
            }

            /* OK! The mode isn't + and it isnt -, search for it! */
            /* Get the index of the mode flag */
            /* We now just look it up on the map ;) */
            if ((mindex = chanmodes->map[(unsigned char)*modes]) == -1) {
                  if (MyClient(sptr)) {
                        send_me_numeric(sptr, ERR_UNKNOWNMODE, *modes);
                  }
                  continue;
            }

            /* Mode index successfully found -- link xMode and chanMode pointers
             * to the appropriate table entry, just to make life easier
             */
            t = &chanmodes->table[mindex]; /* get xMode table entry */
            m = chanmode_tab[mindex]; /* get chanMode entry */

            if (OVERFLOW(0)) {
                  /* Drop the mode change instantly if there is a danger of
                   * overflowing modebuf 
                   */
                  continue;
            }

            ASSERT(m->do_mode != NULL);

            /* Current privilege level must be >= required privilege level, unless
             * level >= CPRIV_ULINE, in which case the source of the mode change is
             * either a U:Lined client, a server, or a sadmin doing SAMODE.
             */
            if (m->privs > level && (level < CPRIV_ULINE)) {
                  /* Exception: mode is a list mode (ie, +b), and there are no
                   * parameters. (For example, a client getting a ban list...)
                   */
                  if (!((m->paratype == CPTYP_ADDRLIST) && (parv[args] == NULL))) {
                        errors |= CM_ERR_NOPRIVS;
                        continue;
                  }
            }

            /* Now check parameter requirements */
            mode_para = NULL;
            switch (m->para) {
                  case CPARA_NONE:
                        break;
                  case CPARA_SETONLY:
                        if (change == XMODE_DEL) {
                              break;
                        }
                        /* fall through */
                  case CPARA_SETUNSET:
                        if ((m->paratype != CPTYP_ADDRLIST) && (m->paratype != CPTYP_NICKLIST)) {
                              if (parv[args] == NULL) {
                                    errors |= CM_ERR_NEEDMORE;
                                    break;
                              }
                              if (++nparams > maxparams) {
                                    /* too many modes with parameters, eat this one */
                                    args++;
                                    continue;
                              }
                        }
                        if (parv[args] != NULL) {
                              mode_para = parv[args];
                              break;
                        }

                        /* Ignore multiple list requests */
                        if (errors & CM_ERR_LISTSENT) {
                              continue;
                        }
                        errors |= CM_ERR_LISTSENT;
                        break;
            }
            if (errors & CM_ERR_NEEDMORE) {
                  continue; /* Fatal for current flag */
            }

            if (m->paratype == CPTYP_NONE) {
                  if ((change == XMODE_ADD) && (chptr->mode.mode & t->mode)) {
                        /* Adding mode that's already set, bounce it */
                        continue;
                  }
                  if ((change == XMODE_DEL) && !(chptr->mode.mode & t->mode)) {
                        /* Removing mode that's not set, bounce it */
                        continue;
                  }
            }

            switch ((*m->do_mode)(cptr, sptr, chptr, mindex, level, change, mode_para, &errors, &changes)) {
                  case CM_STAT_SUCCESS:
                        nmodes++; /* record successful mode change */
                  case CM_STAT_FAILURE:
                        if (mode_para != NULL) {
                              args++;
                        }
                  case CM_STAT_BOUNCE:
                        break;
                  default:
                        ASSERT("unknown do_mode() return" == NULL);
                        break;
            }

            /* CM_ERR_NEEDMORE is mode-specific, so check it on each iteration */
            if (errors & CM_ERR_NEEDMORE) {
                  char need_more[7] = "MODE xx";

                  need_more[5] = (change == XMODE_ADD) ? '+' : '-';
                  need_more[6] = *modes;
                  need_more[7] = '\0';

                  send_me_numeric(sptr, ERR_NEEDMOREPARAMS, need_more);
                  errors &= ~CM_ERR_NEEDMORE;
            }
      }

      /* If our modebuf ends in + or -, truncate it */
      if (modebuf[midx - 1] == '+' || modebuf[midx - 1] == '-') {
            modebuf[--midx] = '\0';
      }

      /* Finally, handle any errors caused during doing the mode changes */
      if (MyClient(sptr)) {
            if (errors & CM_ERR_NOPRIVS) {
                  send_me_numeric(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname);
            }
            if (errors & (CM_ERR_NOTIRCOP|CM_ERR_NOTADMIN|CM_ERR_NOTNETADMIN)) {
                  send_me_numericNA(sptr, ERR_NOPRIVILEGES);
            }
      }

#ifdef CMDEBUG
      Debug((DEBUG_DEBUG, "parse_mode(%s) complete, %d mode changes occured",
            chptr->chname, nmodes));
#endif
      return nmodes; /* return changed mode count */
}

void synch_chan_modes(aClient *sptr, aChannel *chptr)
{
      static int prefix_len = 0;
      char buf[BUFSIZE], *t;
      int idx = 0, n = 0;
      chanMember *cm;

      ASSERT(sptr != NULL);

      if (chptr == NULL) {
            prefix_len = strlen(use_id(sptr, sptr));

            if (!(CapID(sptr) && HasSUID(sptr))) {
                  prefix_len += 5; /* SJOIN */
            }

            prefix_len += 8; /* space, space, prefix, etc */
            return;
      }

      if (*chptr->chname != '#') {
            return;
      }

      midx = pidx = 0;
      modebuf[0] = parabuf[0] = '\0';
      get_chan_modes(sptr, chptr, modebuf, parabuf);

      buf[idx] = '\0';
      if (*parabuf != '\0') {
            idx = ircsprintf(buf, CapID(sptr) ? "%B %s %s %s :" : "%ld %s %s %s :",
                  chptr->channelts, chptr->chname, modebuf, parabuf);
      }
      else {
            idx = ircsprintf(buf, CapID(sptr) ? "%B %s %s :" : "%ld %s %s :",
                  chptr->channelts, chptr->chname, modebuf);
      }

      for (cm = chptr->members; cm != NULL; cm = cm->nextuser) {
            if (cm->flags & CMODE_CHANADMIN) {
                  buf[idx++] = '*';
            }
            if (cm->flags & CMODE_CHANOP) {
                  buf[idx++] = '@';
            }
            if (cm->flags & CMODE_HALFOP) {
                  buf[idx++] = '%';
            }
            if (cm->flags & CMODE_VOICE) {
                  buf[idx++] = '+';
            }

            t = use_id(cm->cptr, sptr);
            while (*t != '\0') {
                  buf[idx++] = *t++;
            }
            buf[idx++] = ' ';
            n++;

            if ((idx + prefix_len + NICKLEN + 3) > BUFSIZE) {
                  if (buf[idx - 1] == ' ') {
                        idx--;
                  }
                  buf[idx] = '\0';

                  sendto_one_client_nopostfix(sptr, &me, &CMD_SJOIN, "%s", buf);
                  idx = ircsprintf(buf, "%ld %s 0 :", chptr->channelts, chptr->chname);
                  n = 0;
            }
      }

      if (n) {
            if (buf[idx - 1] == ' ') {
                  idx--;
            }
            buf[idx] = '\0';

            sendto_one_client_nopostfix(sptr, &me, &CMD_SJOIN, "%s", buf);
      }

      synch_ban_list(sptr, chptr, CMODE_BAN);
      synch_ban_list(sptr, chptr, CMODE_EXCEPT);
      synch_ban_list(sptr, chptr, CMODE_INVEX);
}

/* SJOIN callbacks */

void sj_domode_pbuf_l(int mindex, short change, Mode *mode, char *para, int buffer_it)
{
      if (buffer_it) {
            if (mode->limit) {
                  ADD_PARA(my_itoa(mode->limit));
            }
      }
      else {
            mode->limit = atoi(para);
      }
}

void sj_domode_pbuf_k(int mindex, short change, Mode *mode, char *para, int buffer_it)
{
      if (buffer_it) {
            if (*mode->key != '\0') {
                  ADD_PARA(mode->key);
            }
      }
      else {
            strncpyzt(mode->key, para, KEYLEN + 1);
      }
}

int sj_cmp_para_l(int mindex, Mode *old_mode, Mode *new_mode)
{
      if (old_mode->limit > new_mode->limit) {
            return CM_SJCMP_KEEPOLD;
      }
      if (old_mode->limit < new_mode->limit) {
            return CM_SJCMP_KEEPNEW;
      }
      return CM_SJCMP_EQUAL;
}

int sj_cmp_para_k(int mindex, Mode *old_mode, Mode *new_mode)
{
      char *old = old_mode->key;
      char *new = new_mode->key;
      int diff = strcmp(old, new);

      if (*old && (!*new || (*new && diff > 0))) {
            return CM_SJCMP_KEEPOLD;
      }
      if (!*old || (*old && diff < 0)) {
            return CM_SJCMP_KEEPNEW;
      }
      return CM_SJCMP_EQUAL;
}

void sj_synch_para_l(int mindex, Mode *dest_mode, Mode *src_mode)
{
      dest_mode->limit = src_mode->limit;
}

void sj_synch_para_k(int mindex, Mode *dest_mode, Mode *src_mode)
{
      strncpyzt(dest_mode->key, src_mode->key, KEYLEN + 1);
}

Generated by  Doxygen 1.6.0   Back to index