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

channel.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.c,v 1.109.2.1 2004/12/07 12:17:37 pneumatus Exp $
 */

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

static int flush_safelists(HookData *);
static HookEvent *he_flush_safelists;

aChannel *channel = NULL;
BlockHeap *channel_heap = NULL;
BlockHeap *chanmember_heap = NULL;

const char *msg_errors[] = {
      NULL,
      "channel is moderated",
      "external messages not allowed",
      "you need a registered nickname",
      "you're banned",
      "colour is not allowed",
      "DCC commands are not allowed",
      "CTCP commands are not allowed"
};

void init_channels()
{
      channel_heap = BlockHeapCreate(sizeof(aChannel), CHANNEL_HEAP_SIZE);
      chanmember_heap = BlockHeapCreate(sizeof(chanMember), CHANMEMBER_HEAP_SIZE);

      he_flush_safelists = hook_add_event(h_pre_netio, flush_safelists);
}

aClient *find_chasing(aClient *sptr, char *user, int *chasing, char *cmd)
{
      aClient *acptr = find_client(user, NULL);

      if (chasing) {
            *chasing = 0;
      }
      if (acptr !=  NULL) {
            return acptr;
      }
      if ((acptr = get_history(user, (long)KILLCHASETIMELIMIT)) == NULL) {
            if (!IsServer(sptr)) {
                  target_left(sptr, user, cmd, NULL);
            }
            return NULL;
      }
      if (chasing) {
            *chasing = 1;
      }
      return acptr;
}

chanMember *add_user_to_channel(aChannel *chptr, aClient *cptr, int flags)
{
      chanMember *cm;

      ASSERT(cptr != NULL);
      ASSERT(cptr->user != NULL);

      cm = make_chanmember();
      cm->flags = flags;
      cm->cptr = cptr;
      cm->chptr = chptr;
      cm->nextuser = chptr->members;
      cm->nextchan = cptr->user->channel;
      cm->banserial = chptr->banserial;

      chptr->members = cm;
      chptr->users++;

      cptr->user->channel = cm;
      cptr->user->joined++;
      return cm;
}

void sub1_from_channel(aChannel *chptr)
{
      SLink *tmp;
      dlink_node *node, *next = NULL;
      channelBan *ban;

      if (--chptr->users > 0) {
            return;
      }

      while ((tmp = chptr->invites) != NULL) {
            del_invite(tmp->value.cptr, chptr);
      }
      DLINK_FOREACH_SAFE_DATA(chptr->banlist.head, node, next, ban, channelBan) {
            dlink_del(&chptr->banlist, NULL, node);
            destroy_channelban(ban);
      }
      DLINK_FOREACH_SAFE_DATA(chptr->exceptlist.head, node, next, ban, channelBan) {
            dlink_del(&chptr->exceptlist, NULL, node);
            destroy_channelban(ban);
      }
      DLINK_FOREACH_SAFE_DATA(chptr->invexlist.head, node, next, ban, channelBan) {
            dlink_del(&chptr->invexlist, NULL, node);
            destroy_channelban(ban);
      }

      if (chptr->prevch != NULL) {
            chptr->prevch->nextch = chptr->nextch;
      }
      else {
            channel = chptr->nextch;
      }
      if (chptr->nextch != NULL) {
            chptr->nextch->prevch = chptr->prevch;
      }
      del_from_channel_hash_table(chptr->chname, chptr);
#ifdef FLUD
      free_fluders(NULL, chptr);
#endif
      free_channel(chptr);
      Count.chan--;
}

chanMember *remove_user_from_channel(aClient *cptr, aChannel *chptr)
{
      chanMember **curr, *tmp;

      for (curr = &chptr->members; (tmp = *curr); curr = &tmp->nextuser) {
            if (tmp->cptr == cptr) {
                  *curr = tmp->nextuser;
                  break;
            }
      }
      for (curr = &cptr->user->channel; (tmp = *curr); curr = &tmp->nextchan) {
            if (tmp->chptr == chptr) {
                  *curr = tmp->nextchan;
                  free_chanmember(tmp);
                  break;
            }
      }
      cptr->user->joined--;
      sub1_from_channel(chptr);
      return *curr;
}

int can_send(aClient *cptr, aChannel *chptr, char *msg)
{
      chanMember *cm;

      if (IsServer(cptr) || IsULine(cptr)) {
            return 0;
      }
      if (HasMode(cptr, UMODE_SADMIN) && !GeneralConfig.restrict_chan_override) {
            return 0;
      }

      if ((cm = find_user_member(cptr->user->channel, chptr)) == NULL) {
            if (chptr->mode.mode & CMODE_MODERATED) {
                  return CANT_SEND_MODERATED;
            }
            if (chptr->mode.mode & CMODE_NOPRIVMSGS) {
                  return CANT_SEND_NOPRIVMSGS;
            }
            if ((chptr->mode.mode & CMODE_MODREGONLY) && !HasMode(cptr, UMODE_REGNICK)) {
                  return CANT_SEND_MODREGONLY;
            }
            if (MyClient(cptr) && is_banned(cptr, chptr, NULL)) {
                  return CANT_SEND_BANNED;
            }
            if ((chptr->mode.mode & CMODE_NOCOLOUR) && msg_has_colour(msg)) {
                  return CANT_SEND_COLOUR;
            }
      }
      else if (!(cm->flags & (CMODE_CHANADMIN|CMODE_CHANOP|CMODE_HALFOP|CMODE_VOICE))) {
            if (chptr->mode.mode & CMODE_MODERATED) {
                  return CANT_SEND_MODERATED;
            }
            if ((chptr->mode.mode & CMODE_MODREGONLY) && !HasMode(cptr, UMODE_REGNICK)) {
                  return CANT_SEND_MODREGONLY;
            }
            if (is_banned(cptr, chptr, cm)) {
                  return CANT_SEND_BANNED;
            }
            if ((chptr->mode.mode & CMODE_NOCOLOUR) && msg_has_colour(msg)) {
                  return CANT_SEND_COLOUR;
            }
      }

      return 0;
}

int check_channel_name(aClient *cptr, char *chname)
{
      char *p;

      ASSERT(cptr != NULL);
      ASSERT(!BadPtr(chname));

      for (p = chname; *p != '\0'; p++) {
            if (!IsChanChar(*p)) {
                  return 0;
            }
      }
                        
      return 1;
}

int check_fake_channel_name(aClient *cptr, char *chname)
{
      char *p;

      if (!GeneralConfig.allow_fake_channels) {
            for (p = chname; *p != '\0'; p++) {
                  if (!IsChanChar(*p) || IsFakeChanChar(*p)) {
                        return 0;
                  }
            }
      }
      else {
            for (p = chname; *p != '\0'; p++) {
                  if (!IsChanChar(*p)) {
                        return 0;
                  }
            }
      }

      return 1;
}

int can_join(aClient *sptr, aChannel *chptr, char *key)
{
      SLink *lp;
      int error = 0;

      ASSERT(sptr != NULL);
      ASSERT(sptr->localUser != NULL);

      if (IsULine(sptr)) {
            return 0;
      }
      for (lp = sptr->localUser->invited; lp != NULL; lp = lp->next) {
            if (lp->value.chptr == chptr) {
                  return 0;
            }
      }
      if (HasMode(sptr, UMODE_SADMIN) && !GeneralConfig.restrict_chan_override) {
            return 0;
      }

      if (chptr->mode.mode & CMODE_INVITEONLY) {
            error = ERR_INVITEONLYCHAN;
      }
      if (chptr->mode.mode & CMODE_REGONLY && !HasMode(sptr, UMODE_REGNICK)) {
            error = ERR_NEEDREGGEDNICK;
      }
      if (chptr->mode.mode & CMODE_OPERONLY && !HasMode(sptr, UMODE_OPER)) {
            error = ERR_NOPRIVILEGES;
      }
      if (chptr->mode.mode & CMODE_ADMINONLY && !HasMode(sptr, UMODE_NETADMIN|UMODE_ADMIN)) {
            error = ERR_NOPRIVILEGES;
      }
      if (*chptr->mode.key && (BadPtr(key) || irccmp(chptr->mode.key, key))) {
            error = ERR_BADCHANNELKEY;
      }
      if (chptr->mode.limit && chptr->users >= chptr->mode.limit) {
            error = ERR_BADCHANNELKEY;
      }

      /* Optimisation: only check invex if there was a blocking error */
      if (error && is_invited(sptr, chptr)) {
            error = 0;
      }

      /* Optimisation: only check bans if there was no blocking error */
      if (!error && is_banned(sptr, chptr, NULL)) {
            error = ERR_BANNEDFROMCHAN;
      }

      return error;
}

aChannel *get_channel(aClient *cptr, char *chname, int flag, int *created)
{
      aChannel *chptr = NULL;
      int len;

      if (created != NULL) {
            *created = 0;
      }
      if (BadPtr(chname)) {
            return NULL;
      }

      len = strlen(chname);
      if (MyClient(cptr) && len > CHANNELLEN) {
            chname[CHANNELLEN] = '\0';
            len = CHANNELLEN;
      }
      if ((chptr = find_channel(chname, NULL)) == NULL && (flag == CREATE)) {
            chptr = make_channel();

            if (created != NULL) {
                  *created = 1;
            }

            strncpyzt(chptr->chname, chname, len + 1);

            if (channel != NULL) {
                  channel->prevch = chptr;
            }
            chptr->prevch = NULL;
            chptr->nextch = channel;
            channel = chptr;
            chptr->channelts = timeofday;
            add_to_channel_hash_table(chname, chptr);
            Count.chan++;
      }
      return chptr;
}

static int list_length(SLink *lp)
{
      int count;
      for (count = 0; lp != NULL; lp = lp->next) {
            count++;
      }
      return count;
}

void add_invite(aClient *cptr, aChannel *chptr)
{
      SLink *inv, **tmp;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localUser != NULL);

      del_invite(cptr, chptr);
      if (list_length(cptr->localUser->invited) >= GeneralConfig.max_chans_per_user) {
            del_invite(cptr, cptr->localUser->invited->value.chptr);
      }

      inv = make_slink();
      inv->value.cptr = cptr;
      inv->next = chptr->invites;
      chptr->invites = inv;

      for (tmp = &(cptr->localUser->invited); *tmp != NULL; tmp = &((*tmp)->next));
      inv = make_slink();
      inv->value.chptr = chptr;
      inv->next = NULL;
      *tmp = inv;
}

void del_invite(aClient *cptr, aChannel *chptr)
{
      SLink **inv, *tmp;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localUser != NULL);
      ASSERT(chptr != NULL);

      for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next) {
            if (tmp->value.cptr == cptr) {
                  *inv = tmp->next;
                  free_slink(tmp);
                  break;
            }
      }
      for (inv = &(cptr->localUser->invited); (tmp = *inv); inv = &tmp->next) {
            if (tmp->value.chptr == chptr) {
                  *inv = tmp->next;
                  free_slink(tmp);
                  break;
            }
      }
}

void send_topic_burst(aClient *cptr)
{
      aChannel *chptr;
      aClient *acptr;

      ASSERT(cptr != NULL);

      for (chptr = channel; chptr != NULL; chptr = chptr->nextch) {
            if (BadPtr(chptr->topic)) {
                  continue;
            }
            sendto_one_client_nopostfix(cptr, &me, &CMD_TOPIC, "%s %s %ld :%s", chptr->chname,
                  chptr->topic_nick, chptr->topic_time, chptr->topic);
      }
      for (acptr = client; acptr != NULL; acptr = acptr->next) {
            if (!IsPerson(acptr) || acptr->from == cptr || BadPtr(acptr->user->away)) {
                  continue;
            }
            sendto_one_client_nopostfix(cptr, acptr, &CMD_AWAY, ":%s", acptr->user->away);
      }
}

void send_list(aClient *sptr, int numsend)
{
      char mode_buf[REALMODEBUFLEN], para_buf[REALMODEBUFLEN];
      char modestuff[TOPICLEN + 3 + KEYLEN + 8 + 3 + 1], tmpbuf[3 + KEYLEN + 8 + 3 + 1];
      LOpts *lopt;
      aChannel *chptr;
      int hashnum;

      ASSERT(sptr != NULL);
      ASSERT(sptr->localUser != NULL);

      lopt = sptr->localUser->lopt;

      for (hashnum = lopt->starthash; (hashnum < CH_MAX) && (numsend > 0); hashnum++) {
            for (chptr = hash_get_chan_bucket(hashnum); chptr != NULL; chptr = chptr->hnextch) {
                  if (SecretChannel(chptr) && !IsMember(sptr, chptr)
                    && (!HasMode(sptr, UMODE_SADMIN) || !GeneralConfig.restrict_chan_override)) {
                        continue;
                  }
                  if ((!lopt->showall) && ((chptr->users < lopt->usermin) || ((lopt->usermax >= 0) &&
                    (chptr->users > lopt->usermax)) || ((chptr->channelts||1) < lopt->chantimemin) ||
                    (chptr->topic_time < lopt->topictimemin) || (chptr->channelts > lopt->chantimemax) ||
                    (chptr->topic_time > lopt->topictimemax) || (lopt->nolist &&
                    find_str_link(lopt->nolist, chptr->chname)) || (lopt->yeslist && 
                    !find_str_link(lopt->yeslist, chptr->chname)))) {
                        continue;
                  }
                  if (HasMode(sptr, UMODE_SADMIN)) {
                        get_chan_modes(sptr, chptr, mode_buf, para_buf);

                        ircsprintf(tmpbuf, " [%s%s%s]", mode_buf, *para_buf ? " " : "",
                              *para_buf ? para_buf : "");
                        ircsprintf(modestuff, "%-2s %s", tmpbuf, chptr->topic);

                        send_me_numeric(sptr, RPL_LIST, chptr->chname, chptr->users,
                              modestuff);
                  }
                  else if (ShowChannel(sptr, chptr)) {
                        send_me_numeric(sptr, RPL_LIST, chptr->chname, chptr->users,
                              chptr->topic);
                  }
                  numsend--;
            }
      }
      if (hashnum == CH_MAX) {
            SLink *lp, *next;

            send_me_numericNA(sptr, RPL_LISTEND);
            for (lp = lopt->yeslist; lp != NULL; lp = next) {
                  next = lp->next;
                  MyFree(lp->value.cp);
                  free_slink(lp);
            }
            for (lp = lopt->nolist; lp != NULL; lp = next) {
                  next = lp->next;
                  MyFree(lp->value.cp);
                  free_slink(lp);
            }

            MyFree(sptr->localUser->lopt);
            sptr->localUser->lopt = NULL;
            dlink_del(&listingcli_list, sptr, NULL);
      }
      else {
            lopt->starthash = hashnum;
      }
}

static int flush_safelists(HookData *hdata)
{
      dlink_node *node, *next = NULL;
      aClient *cptr;

      DLINK_FOREACH_SAFE_DATA(listingcli_list.head, node, next, cptr, aClient) {
            while (DoList(cptr) && IsSendable(cptr)) {
                  send_list(cptr, 64);
            }
      }
      return 0;
}

int check_for_spambot(aClient *sptr, char *chname)
{
      int part_delta;

      ASSERT(sptr != NULL);
      ASSERT(sptr->localUser != NULL);
      ASSERT(MyConnect(sptr));
      ASSERT(FloodConfig.max_join_part_count > 0);

      if (sptr->localUser->join_part_count >= FloodConfig.max_join_part_count) {
            if (sptr->localUser->oper_warn_countdown) {
                  sptr->localUser->oper_warn_countdown--;
            }
            if (!sptr->localUser->oper_warn_countdown) {
                  if (!BadPtr(chname)) {
                        sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) joining %s is a possible spambot",
                              sptr->name, sptr->username, MaskedHost(sptr), chname);
                  }
                  else {
                        sendto_realops_lev(SPAM_LEV, "User %s (%s@%s) is a possible spambot",
                              sptr->name, sptr->username, MaskedHost(sptr));
                  }
                  sptr->localUser->oper_warn_countdown = OPER_SPAM_COUNTDOWN;
            }
            return 0;
      }

      part_delta = (timeofday - sptr->localUser->last_part_time);

      if (FloodConfig.spambot_squelch_time && (part_delta > FloodConfig.spambot_squelch_time)) {
            int join_part_decrease = (part_delta / FloodConfig.spambot_squelch_time);

            if (join_part_decrease > sptr->localUser->join_part_count) {
                  sptr->localUser->join_part_count = 0;
            }
            else {
                  sptr->localUser->join_part_count -= join_part_decrease;
            }
      }
      else if (FloodConfig.min_join_part_time > (timeofday - sptr->localUser->last_join_time)) {
            sptr->localUser->join_part_count++;
      }

      return 1;
}

Generated by  Doxygen 1.6.0   Back to index