Esempio n. 1
0
void relay_private_notice(struct Client* sptr, const char* name, const char* text)
{
  struct Client* acptr;
  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);

  if (0 == (acptr = FindUser(name)))
    return;
  if (IsOnlyreg(acptr) && !IsRegnick(sptr)) {
	  send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr));
	  return;
  }

  if (!IsChannelService(acptr))
  {
    if (check_target_limit(sptr, acptr, cli_name(acptr), 0))
    {
      return;
    }
    if (is_silenced(sptr, acptr))
    {
      send_reply(sptr, ERR_SILENCED, cli_name(acptr));
      return;
    }
  }
    
  /*
   * deliver the message
   */
  if (MyUser(acptr))
    add_target(acptr, sptr);

  sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text);
}
Esempio n. 2
0
void server_relay_private_message(struct Client* sptr, const char* name, const char* text)
{
  struct Client* acptr;
  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);
  /*
   * nickname addressed?
   */
  if (0 == (acptr = findNUser(name)) || !IsUser(acptr)) {
    send_reply(sptr, SND_EXPLICIT | ERR_NOSUCHNICK, "* :Target left %s. "
	       "Failed to deliver: [%.20s]", feature_str(FEAT_NETWORK), text);
    return;
  }

  if (!IsChannelService(sptr) && is_silenced(sptr, acptr))
  {
    send_reply(sptr, ERR_SILENCED, cli_name(acptr));
    return;
  }

  if (IsOnlyreg(acptr) && !IsRegnick(sptr)) {
	  send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr));
	  return;
  }

  if (MyUser(acptr))
    add_target(acptr, sptr);

  sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
}
Esempio n. 3
0
void server_relay_channel_message(struct Client* sptr, const char* name, const char* text)
{
  struct Channel* chptr;
  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);

  if (0 == (chptr = FindChannel(name))) {
    /*
     * XXX - do we need to send this back from a remote server?
     */
    send_reply(sptr, ERR_NOSUCHCHANNEL, name);
    return;
  }
  /*
   * This first: Almost never a server/service
   * Servers may have channel services, need to check for it here
   */
  if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) {
    sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr),
			     SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
  }
  else
    send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname);
}
Esempio n. 4
0
void server_relay_private_notice(struct Client* sptr, const char* name, const char* text)
{
  struct Client* acptr;
  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);
  /*
   * nickname addressed?
   */
  if (0 == (acptr = findNUser(name)) || !IsUser(acptr))
    return;

  if (!IsChannelService(sptr) && is_silenced(sptr, acptr))
  {
    send_reply(sptr, ERR_SILENCED, cli_name(acptr));
    return;
  }

  if (IsOnlyreg(acptr) && !IsRegnick(sptr)) {
	  send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr));
	  return;
  }

  if (MyUser(acptr))
    add_target(acptr, sptr);

  sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text);
}
Esempio n. 5
0
int
ms_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Channel *chptr = 0;
  struct ModeBuf mbuf;
  struct Membership *member = 0;

  if (parc < 3)
    return need_more_params(sptr, "MODE");

  if (IsLocalChannel(parv[1]))
    return 0;

  if (!(chptr = FindChannel(parv[1])))
    return set_user_mode(cptr, sptr, parc, parv);

  ClrFlag(sptr, FLAG_TS8);

  if (IsServer(sptr)) {
    if (cli_uworld(sptr))
      modebuf_init(&mbuf, sptr, cptr, chptr,
		   (MODEBUF_DEST_CHANNEL | /* Send mode to clients */
		    MODEBUF_DEST_SERVER  | /* Send mode to servers */
		    MODEBUF_DEST_HACK4));  /* Send a HACK(4) message */
    else
      modebuf_init(&mbuf, sptr, cptr, chptr,
		   (MODEBUF_DEST_CHANNEL | /* Send mode to clients */
		    MODEBUF_DEST_SERVER  | /* Send mode to servers */
		    MODEBUF_DEST_HACK3));  /* Send a HACK(3) message */

    mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
	       (MODE_PARSE_SET    | /* Set the mode */
		MODE_PARSE_STRICT | /* Interpret it strictly */
		MODE_PARSE_FORCE), NULL); /* And force it to be accepted */
  } else {
    if (!IsChannelService(sptr) && (!(member = find_member_link(chptr, sptr)) || (!IsChanOp(member) && !IsHalfOp(member)))) {
      modebuf_init(&mbuf, sptr, cptr, chptr,
		   (MODEBUF_DEST_SERVER |  /* Send mode to server */
		    MODEBUF_DEST_HACK2  |  /* Send a HACK(2) message */
		    MODEBUF_DEST_DEOP   |  /* Deop the source */
		    MODEBUF_DEST_BOUNCE)); /* And bounce the MODE */
      mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
		 (MODE_PARSE_STRICT |  /* Interpret it strictly */
		  MODE_PARSE_BOUNCE), member); /* And bounce the MODE */
    } else {
      modebuf_init(&mbuf, sptr, cptr, chptr,
		   (MODEBUF_DEST_CHANNEL | /* Send mode to clients */
		    MODEBUF_DEST_SERVER)); /* Send mode to servers */
      mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
		 (MODE_PARSE_SET    | /* Set the mode */
		  MODE_PARSE_STRICT | /* Interpret it strictly */
		  MODE_PARSE_FORCE), member); /* And force it to be accepted */
    }
  }

  return modebuf_flush(&mbuf);
}
/*
 * mo_kill - oper message handler
 *
 * NOTE: IsPrivileged(sptr), IsAnOper(sptr) == true
 *       IsServer(cptr), IsServer(sptr) == false
 *
 * parv[0]      = sender prefix
 * parv[1]      = kill victim
 * parv[parc-1] = kill path
 */
int mo_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client* victim;
  char*          user;
  char           msg[TOPICLEN + 3]; /* (, ), and \0 */

  assert(0 != cptr);
  assert(0 != sptr);
  /*
   * oper connection to this server, cptr is always sptr
   */
  assert(cptr == sptr);
  assert(IsAnOper(sptr));

  if (parc < 3 || EmptyString(parv[parc - 1]))
    return need_more_params(sptr, "KILL");

  user = parv[1];
  ircd_snprintf(0, msg, sizeof(msg), "(%.*s)", TOPICLEN, parv[parc - 1]);

  if (!(victim = FindClient(user))) {
    /*
     * If the user has recently changed nick, we automaticly
     * rewrite the KILL for this new nickname--this keeps
     * servers in synch when nick change and kill collide
     */
    if (!(victim = get_history(user, (long)15)))
      return send_reply(sptr, ERR_NOSUCHNICK, user);

    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Changed KILL %s into %s", sptr,
		  user, cli_name(victim));
  }
  if (!HasPriv(sptr, MyConnect(victim) ? PRIV_LOCAL_KILL : PRIV_KILL))
    return send_reply(sptr, ERR_NOPRIVILEGES);

  if (IsServer(victim) || IsMe(victim)) {
    return send_reply(sptr, ERR_CANTKILLSERVER);
  }
  /*
   * if the user is +k, prevent a kill from local user
   */
  if (IsChannelService(victim))
    return send_reply(sptr, ERR_ISCHANSERVICE, "KILL", cli_name(victim));


  if (!MyConnect(victim) && !HasPriv(sptr, PRIV_KILL)) {
    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Nick %s isnt on your server",
		  sptr, cli_name(victim));
    return 0;
  }
  return do_kill(cptr, sptr, victim, cli_user(sptr)->host, cli_name(sptr),
		 msg);
}
Esempio n. 7
0
void relay_directed_notice(struct Client* sptr, char* name, char* server, const char* text)
{
  struct Client* acptr;
  char*          host;

  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);
  assert(0 != server);

  if (0 == (acptr = FindServer(server + 1)))
    return;
  /*
   * NICK[%host]@server addressed? See if <server> is me first
   */
  if (!IsMe(acptr)) {
    sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text);
    return;
  }
  /*
   * Look for an user whose NICK is equal to <name> and then
   * check if it's hostname matches <host> and if it's a local
   * user.
   */
  *server = '\0';
  if ((host = strchr(name, '%')))
    *host++ = '\0';

  if (!(acptr = FindUser(name)) || !MyUser(acptr) ||
      (!EmptyString(host) && 0 != match(host, cli_user(acptr)->realhost)))
    return;

  *server = '@';
  if (host)
    *--host = '%';

  if (!IsChannelService(sptr) && is_silenced(sptr, acptr))
  {
    send_reply(sptr, ERR_SILENCED, cli_name(acptr));
    return;
  }

  if (IsOnlyreg(acptr) && !IsRegnick(sptr))
    send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr));
  else
    sendcmdto_one(sptr, CMD_NOTICE, acptr, "%s :%s", name, text);
}
Esempio n. 8
0
void server_relay_channel_notice(struct Client* sptr, const char* name, const char* text)
{
  struct Channel* chptr;
  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);

  if (0 == (chptr = FindChannel(name)))
    return;
  /*
   * This first: Almost never a server/service
   * Servers may have channel services, need to check for it here
   */
  if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) {
    sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr),
			     SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text);
  }
}
Esempio n. 9
0
/*
 * ms_svsident - server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = Target numeric
 * parv[2] = New ident
 */
int ms_svsident(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client *acptr;
  char *s;
  char *newident = NULL;
  int legalident=1;

  if (parc < 3)
    return need_more_params(sptr, "SVSIDENT");

   /* Ignore SVSIDENT for a user that has quit */
  if (!(acptr = findNUser(parv[1])))
    return 0;

  if (IsChannelService(acptr))
    return 0;

  newident = strdup(parv[2]);

  if (strlen(newident) > USERLEN)
    return protocol_violation(sptr, "Ident too long in SVSIDENT command");

  for (s = newident; *s; s++)
  {
    if (!IsUserChar(*s))
    {
      legalident = 0;
      break;
    }
  }

  if (legalident == 0)
    return protocol_violation(sptr, "Illegal characters in SVSIDENT ident");

  ircd_strncpy(cli_user(acptr)->username, newident, USERLEN);
  ircd_strncpy(cli_username(acptr), newident, USERLEN);

  sendcmdto_serv_butone(sptr, CMD_SVSIDENT, cptr, "%s%s %s", acptr->cli_user->server->cli_yxx,
        acptr->cli_yxx, newident);

  return 0;
}
Esempio n. 10
0
/*
 * m_kick - generic message handler
 *
 * parv[0] = sender prefix
 * parv[1] = channel
 * parv[2] = client to kick
 * parv[parc-1] = kick comment
 */
int m_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Client *who;
  struct Channel *chptr;
  struct Membership *member = 0;
  struct Membership* member2;
  char *name, *comment;

  ClrFlag(sptr, FLAG_TS8);

  if (parc < 3 || *parv[1] == '\0')
    return need_more_params(sptr, "KICK");

  name = parv[1];

  /* simple checks */
  if (!(chptr = get_channel(sptr, name, CGT_NO_CREATE)))
    return send_reply(sptr, ERR_NOSUCHCHANNEL, name);

  if (!(member2 = find_member_link(chptr, sptr)) || IsZombie(member2)
      || !IsChanOp(member2))
    return send_reply(sptr, ERR_CHANOPRIVSNEEDED, name);

  if (!(who = find_chasing(sptr, parv[2], 0)))
    return 0; /* find_chasing sends the reply for us */

  /* Don't allow the channel service to be kicked */
  if (IsChannelService(who))
    return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname);

  /* Prevent kicking opers from local channels -DM- */
  if (IsLocalChannel(chptr->chname) && HasPriv(who, PRIV_DEOP_LCHAN))
    return send_reply(sptr, ERR_ISOPERLCHAN, cli_name(who), chptr->chname);

  /* check if kicked user is actually on the channel */
  if (!(member = find_member_link(chptr, who)) || IsZombie(member))
    return send_reply(sptr, ERR_USERNOTINCHANNEL, cli_name(who), chptr->chname);

  /* Don't allow to kick member with a higher op-level,
   * or members with the same op-level unless both are MAXOPLEVEL.
   */
  if (OpLevel(member) < OpLevel(member2)
      || (OpLevel(member) == OpLevel(member2)
          && OpLevel(member) < MAXOPLEVEL))
    return send_reply(sptr, ERR_NOTLOWEROPLEVEL, cli_name(who), chptr->chname,
	OpLevel(member2), OpLevel(member), "kick",
	OpLevel(member) == OpLevel(member2) ? "the same" : "a higher");

  /* We rely on ircd_snprintf to truncate the comment */
  comment = EmptyString(parv[parc - 1]) ? parv[0] : parv[parc - 1];

  if (!IsLocalChannel(name))
    sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who,
			  comment);

  if (IsDelayedJoin(member)) {
    /* If it's a delayed join, only send the KICK to the person doing
     * the kicking and the victim */
    if (MyUser(who))
      sendcmdto_one(sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment);
    sendcmdto_one(who, CMD_JOIN, sptr, "%H", chptr);
    sendcmdto_one(sptr, CMD_KICK, sptr, "%H %C :%s", chptr, who, comment);
    CheckDelayedJoins(chptr);
  } else
    sendcmdto_channel_butserv_butone(sptr, CMD_KICK, chptr, NULL, 0, "%H %C :%s", chptr, who,
                                     comment);

  make_zombie(member, who, cptr, sptr, chptr);

  return 0;
}
Esempio n. 11
0
/*
 * ms_kick - server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = channel
 * parv[2] = client to kick
 * parv[parc-1] = kick comment
 */
int ms_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Client *who;
  struct Channel *chptr;
  struct Membership *member = 0, *sptr_link = 0;
  char *name, *comment;

  ClrFlag(sptr, FLAG_TS8);

  if (parc < 3 || *parv[1] == '\0')
    return need_more_params(sptr, "KICK");

  name = parv[1];
  comment = parv[parc - 1];

  /* figure out who gets kicked from what */
  if (IsLocalChannel(name) ||
      !(chptr = get_channel(sptr, name, CGT_NO_CREATE)) ||
      !(who = findNUser(parv[2])))
    return 0;

  /* We go ahead and pass on the KICK for users not on the channel */
  member = find_member_link(chptr, who);
  if (member && IsZombie(member))
  {
    /* We might get a KICK from a zombie's own server because the user
     * net-rode during a burst (which always generates a KICK) *and*
     * was kicked via another server.  In that case, we must remove
     * the user from the channel.
     */
    if (sptr == cli_user(who)->server)
    {
      remove_user_from_channel(who, chptr);
    }
    /* Otherwise, we treat zombies like they are not channel members. */
    member = 0;
  }

  /* Send HACK notice, but not for servers in BURST */
  /* 2002-10-17: Don't send HACK if the users local server is kicking them */
  if (IsServer(sptr) 
      && !IsBurstOrBurstAck(sptr)
      && sptr!=cli_from(who))
    sendto_opmask_butone(0, SNO_HACK4, "HACK: %C KICK %H %C %s", sptr, chptr,
			 who, comment);

  /* Unless someone accepted it downstream (or the user isn't on the channel
   * here), if kicker is not on channel, or if kicker is not a channel
   * operator, bounce the kick
   *
   * Don't bounce the kick if it's coming from a channel service, regardless
   * of channel membership.  -- aaronc
   */
  if (!IsServer(sptr) && !IsChannelService(sptr) && member && cli_from(who) != cptr &&
      (!(sptr_link = find_member_link(chptr, sptr)) || (!IsChanOp(sptr_link) && !IsHalfOp(sptr_link)))) {
    sendto_opmask_butone(0, SNO_HACK2, "HACK: %C KICK %H %C %s", sptr, chptr,
			 who, comment);

    sendcmdto_one(who, CMD_JOIN, cptr, "%H", chptr);

    /* Reop/revoice member */
    if (IsChanOp(member)  || IsHalfOp(member) || HasVoice(member)) {
      struct ModeBuf mbuf;

      modebuf_init(&mbuf, sptr, cptr, chptr,
		   (MODEBUF_DEST_SERVER |  /* Send mode to a server */
		    MODEBUF_DEST_DEOP   |  /* Deop the source */
		    MODEBUF_DEST_BOUNCE)); /* And bounce the MODE */

      if (IsChanOp(member))
	modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, who);
      if (IsHalfOp(member))
        modebuf_mode_client(&mbuf, MODE_DEL | MODE_HALFOP, who);
      if (HasVoice(member))
	modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who);

      modebuf_flush(&mbuf);
    }
  } else {
    /* Propagate kick... */
    sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who,
			  comment);

    if (member) { /* and tell the channel about it */
      sendcmdto_channel_butserv_butone(IsServer(sptr) ? (feature_bool(FEAT_HIS_HIDEWHO) ? &his : &me) : sptr, 
	  		   	       CMD_KICK, chptr, NULL, 0, "%H %C :%s", chptr, who,
				       comment);

      make_zombie(member, who, cptr, sptr, chptr);
    }
  }

  return 0;
}
Esempio n. 12
0
/*
 * m_kick - generic message handler
 *
 * parv[0] = sender prefix
 * parv[1] = channel
 * parv[2] = client to kick
 * parv[parc-1] = kick comment
 */
int m_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Client *who;
  struct Channel *chptr;
  struct Membership *member = 0;
  char *name, *comment;

  ClrFlag(sptr, FLAG_TS8);

  if (parc < 3 || *parv[1] == '\0')
    return need_more_params(sptr, "KICK");

  name = parv[1];

  /* simple checks */
  if (!(chptr = get_channel(sptr, name, CGT_NO_CREATE)))
    return send_reply(sptr, ERR_NOSUCHCHANNEL, name);

  if (!is_chan_op(sptr, chptr)  && !is_half_op(sptr, chptr))
    return send_reply(sptr, ERR_CHANOPRIVSNEEDED, name);

  if (!(who = find_chasing(sptr, parv[2], 0)))
    return 0; /* find_chasing sends the reply for us */

  /* Don't allow the channel service to be kicked */
  /*
   * ASUKA_X:
   * Allow +X'ed users to kick +k'ed, but not U-lined services.
   * --Bigfoot
   */
  if (IsChannelService(who) && IsService(cli_user(who)->server))
    return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname, "a network service");

  if (IsChannelService(who) && !IsXtraOp(sptr) && (who!=sptr))
    return send_reply(sptr, ERR_ISCHANSERVICE, cli_name(who), chptr->chname, "an IRC operator");

  /* Don't allow halfops to kick chanops */
  if (is_chan_op(who, chptr) && is_half_op(sptr, chptr) && !is_chan_op(sptr, chptr))
     return send_reply(sptr, ERR_HALFCANTKICKOP, name);

  /* Prevent kicking opers from local channels -DM- */
  if (IsLocalChannel(chptr->chname) && HasPriv(who, PRIV_DEOP_LCHAN))
    return send_reply(sptr, ERR_ISOPERLCHAN, cli_name(who), chptr->chname);

  /* check if kicked user is actually on the channel */
  if (!(member = find_member_link(chptr, who)) || IsZombie(member))
    return send_reply(sptr, ERR_USERNOTINCHANNEL, cli_name(who), chptr->chname);

  /* We rely on ircd_snprintf to truncate the comment */
  comment = EmptyString(parv[parc - 1]) ? parv[0] : parv[parc - 1];

  if (!IsLocalChannel(name))
    sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who,
			  comment);

  sendcmdto_channel_butserv_butone((IsServer(sptr) ? &me : sptr), CMD_KICK,
				   chptr, NULL, 0, "%H %C :%s", chptr, who,
				   comment);

  make_zombie(member, who, cptr, sptr, chptr);

  return 0;
}
Esempio n. 13
0
void do_join(struct Client *cptr, struct Client *sptr, struct JoinBuf *join,
             struct JoinBuf *create, char *chan, char *key, int level)
{
  struct Channel *chptr;
  struct Gline *gline;

  /* BADCHANed channel */
  if ((gline = gline_find(chan, GLINE_BADCHAN | GLINE_EXACT)) &&
      GlineIsActive(gline) && !IsAnOper(sptr)) {
    send_reply(sptr, ERR_BANNEDFROMCHAN, chan);
    return;
  }

  /* Bouncy +L joins */
  if (level > feature_int(FEAT_MAX_BOUNCE)) {
    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :*** Couldn't join %s ! - Redirection (+L) setting was too bouncy", sptr, chan);
    return;
  }

  if (!(chptr = FindChannel(chan))) {
    if (((chan[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS))
        || strlen(chan) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) {
      send_reply(sptr, ERR_NOSUCHCHANNEL, chan);
      return;
    }

    if (feature_bool(FEAT_CHANNEL_CREATE_IRCOPONLY) && !IsAnOper(sptr) && !IsChannelService(sptr)) {
      send_reply(sptr, ERR_NOSUCHCHANNEL, chan);
      return;
    }

    if (!(chptr = get_channel(sptr, chan, CGT_CREATE)))
      return;

    /* Try to add the new channel as a recent target for the user. */
    if (check_target_limit(sptr, chptr, chptr->chname, 0)) {
      chptr->members = 0;
      destruct_channel(chptr);
      return;
    }

    joinbuf_join(create, chptr, CHFL_CHANOP | CHFL_CHANNEL_MANAGER);
  } else if (find_member_link(chptr, sptr)) {
    return; /* already on channel */
  } else if (check_target_limit(sptr, chptr, chptr->chname, 0)) {
    return;
  } else {
    int flags = CHFL_DEOPPED;
    int err = 0;
    int excepted = 0;
    int exceptkli = 0;
    struct Ban *ban = NULL;

    if (*chptr->mode.redir && (*chptr->mode.redir != '\0')) {
      if (chptr->users >= chptr->mode.limit) {
        if (IsNoLink(sptr))
          send_reply(sptr, ERR_LINKCHAN, chptr->chname, chptr->mode.redir);
        else if (!IsChannelName(chptr->mode.redir) || !strIsIrcCh(chptr->mode.redir))
          send_reply(sptr, ERR_NOSUCHCHANNEL, chptr->mode.redir);
        else {
          send_reply(sptr, ERR_LINKSET, chptr->chname, chptr->chname, chptr->mode.redir);
          do_join(cptr, sptr, join, create, chptr->mode.redir, key, level+1);
        }
        return;
      }
    }

    if (find_ban(sptr, chptr->exceptlist, EBAN_EXCEPTLIST, 0)) {
      if (feature_bool(FEAT_CHMODE_e_CHMODEEXCEPTION))
        exceptkli = 1;
      excepted = 1;
    }

    /* Check Apass/Upass -- since we only ever look at a single
     * "key" per channel now, this hampers brute force attacks. */
    if (feature_bool(FEAT_CHMODE_Z_STRICT) && (chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr))
      err = ERR_SSLONLYCHAN;
    else if (key && !strcmp(key, chptr->mode.apass))
      flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER;
    else if (key && !strcmp(key, chptr->mode.upass))
      flags = CHFL_CHANOP;
    else if (chptr->users == 0 && !chptr->mode.apass[0] && !(chptr->mode.exmode & EXMODE_PERSIST)) {
      /* Joining a zombie channel (zannel): give ops and increment TS. */
      flags = CHFL_CHANOP;
      chptr->creationtime++;
    } else if (IsXtraOp(sptr)) {
      /* XtraOp bypasses all other checks. */
    } else if ((chptr->mode.exmode & EXMODE_SSLONLY) && !IsSSL(sptr))
      err = ERR_SSLONLYCHAN;
    else if (IsInvited(sptr, chptr)) {
      /* Invites bypass these other checks. */
    } else if (*chptr->mode.key && (!key || strcmp(key, chptr->mode.key)) && !exceptkli)
      err = ERR_BADCHANNELKEY;
    else if (*chptr->mode.key && feature_bool(FEAT_FLEXIBLEKEYS) && (key && !strcmp(key, chptr->mode.key))) {
      /* Assume key checked by previous condition was found to be correct
         and allow join because FEAT_FLEXIBLEKEYS was enabled */
    } else if ((chptr->mode.mode & MODE_INVITEONLY) && !exceptkli)
      err = ERR_INVITEONLYCHAN;
    else if (chptr->mode.limit && (chptr->users >= chptr->mode.limit) && !exceptkli)
      err = ERR_CHANNELISFULL;
    else if ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(sptr))
      err = ERR_NEEDREGGEDNICK;
    else if ((chptr->mode.exmode & EXMODE_ADMINONLY) && !IsAdmin(sptr))
      err = ERR_ADMINONLYCHAN;
    else if ((chptr->mode.exmode & EXMODE_OPERONLY) && !IsAnOper(sptr))
      err = ERR_OPERONLYCHAN;
    else if ((ban = find_ban(sptr, chptr->banlist, EBAN_NONE, 0)) && !excepted)
      err = ERR_BANNEDFROMCHAN;

    /* An oper with WALK_LCHAN privilege can join a local channel
     * he otherwise could not join by using "OVERRIDE" as the key.
     * This will generate a HACK(4) notice, but fails if the oper
     * could normally join the channel. */
    if (IsLocalChannel(chptr->chname)
        && HasPriv(sptr, PRIV_WALK_LCHAN)
        && !(flags & CHFL_CHANOP)
        && key && !strcmp(key, "OVERRIDE"))
    {
      switch (err) {
      case 0:
        if (strcmp(chptr->mode.key, "OVERRIDE")
            && strcmp(chptr->mode.apass, "OVERRIDE")
            && strcmp(chptr->mode.upass, "OVERRIDE")) {
          send_reply(sptr, ERR_DONTCHEAT, chptr->chname);
          return;
        }
        break;
      case ERR_INVITEONLYCHAN: err = 'i'; break;
      case ERR_CHANNELISFULL:  err = 'l'; break;
      case ERR_BANNEDFROMCHAN: err = 'b'; break;
      case ERR_BADCHANNELKEY:  err = 'k'; break;
      case ERR_NEEDREGGEDNICK: err = 'r'; break;
      case ERR_ADMINONLYCHAN:  err = 'a'; break;
      case ERR_OPERONLYCHAN:   err = 'O'; break;
      case ERR_SSLONLYCHAN:    err = 'Z'; break;
      default: err = '?'; break;
      }
      /* send accountability notice */
      if (err)
        sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H "
                             "(overriding +%c)", sptr, chptr, err);
      err = 0;
    }

    /* Is there some reason the user may not join? */
    if (err) {
      switch(err) {
        case ERR_NEEDREGGEDNICK:
          send_reply(sptr,
                     ERR_NEEDREGGEDNICK,
                     chptr->chname,
                     feature_str(FEAT_URLREG));
          break;
        default:
          send_reply(sptr, err, chptr->chname);
          break;
      }
      return;
    }

    joinbuf_join(join, chptr, flags);
    if (flags & CHFL_CHANOP) {
      struct ModeBuf mbuf;
      /* Always let the server op him: this is needed on a net with older servers
         because they 'destruct' channels immediately when they become empty without
         sending out a DESTRUCT message. As a result, they would always bounce a mode
         (as HACK(2)) when the user ops himself.
         (There is also no particularly good reason to have the user op himself.)
      */
      modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER);
      modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr,
                          chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL);
      modebuf_flush(&mbuf);
    }
  }

  del_invite(sptr, chptr);

  if (chptr->topic[0]) {
    send_reply(sptr, RPL_TOPIC, chptr->chname, chptr->topic);
    send_reply(sptr, RPL_TOPICWHOTIME, chptr->chname, chptr->topic_nick,
               chptr->topic_time);
  }

  do_names(sptr, chptr, NAMES_ALL|NAMES_EON); /* send /names list */
}
Esempio n. 14
0
/*
 * Send whois information for acptr to sptr
 */
static void do_whois(struct Client* sptr, struct Client *acptr, int parc)
{
  struct Client *a2cptr=0;
  struct Channel *chptr=0;
  int mlen;
  int len;
  static char buf[512];
  
  const struct User* user = cli_user(acptr);
  const char* name = (!*(cli_name(acptr))) ? "?" : cli_name(acptr);  
  a2cptr = feature_bool(FEAT_HIS_WHOIS_SERVERNAME) && !IsAnOper(sptr)
      && sptr != acptr ? &his : user->server;
  assert(user);
  send_reply(sptr, RPL_WHOISUSER, name, user->username, user->host,
		   cli_info(acptr));

  /* Display the channels this user is on. */
  if (!IsChannelService(acptr))
  {
    struct Membership* chan;
    mlen = strlen(cli_name(&me)) + strlen(cli_name(sptr)) + 12 + strlen(name);
    len = 0;
    *buf = '\0';
    for (chan = user->channel; chan; chan = chan->next_channel)
    {
       chptr = chan->channel;
       
       if (!ShowChannel(sptr, chptr)
           && !(IsOper(sptr) && IsLocalChannel(chptr->chname)))
          continue;

       if (acptr != sptr && IsZombie(chan))
          continue;

       /* Don't show local channels when HIS is defined, unless it's a
	* remote WHOIS --ULtimaTe_
	*/
       if (IsLocalChannel(chptr->chname) && (acptr != sptr) && (parc == 2)
           && feature_bool(FEAT_HIS_WHOIS_LOCALCHAN) && !IsAnOper(sptr))
	  continue;

       if (len+strlen(chptr->chname) + mlen > BUFSIZE - 5)
       {
          send_reply(sptr, SND_EXPLICIT | RPL_WHOISCHANNELS, "%s :%s", name, buf);
          *buf = '\0';
          len = 0;
       }
       if (IsDeaf(acptr))
         *(buf + len++) = '-';
       if (!ShowChannel(sptr, chptr))
         *(buf + len++) = '*';
       if (IsDelayedJoin(chan) && (sptr != acptr))
         *(buf + len++) = '<';
       else if (IsChanOp(chan))
         *(buf + len++) = '@';
       else if (HasVoice(chan))
         *(buf + len++) = '+';
       else if (IsZombie(chan))
         *(buf + len++) = '!';
       if (len)
          *(buf + len) = '\0';
       strcpy(buf + len, chptr->chname);
       len += strlen(chptr->chname);
       strcat(buf + len, " ");
       len++;
     }
     if (buf[0] != '\0')
        send_reply(sptr, RPL_WHOISCHANNELS, name, buf);
  }

  send_reply(sptr, RPL_WHOISSERVER, name, cli_name(a2cptr),
             cli_info(a2cptr));

  if (user)
  {
    if (user->away)
       send_reply(sptr, RPL_AWAY, name, user->away);

    if (SeeOper(sptr,acptr))
       send_reply(sptr, RPL_WHOISOPERATOR, name);

    if (IsAccount(acptr))
      send_reply(sptr, RPL_WHOISACCOUNT, name, user->account);

    if (HasHiddenHost(acptr) && (IsAnOper(sptr) || acptr == sptr))
      send_reply(sptr, RPL_WHOISACTUALLY, name, user->username,
                 user->realhost, ircd_ntoa(&cli_ip(acptr)));

    /* Hint: if your looking to add more flags to a user, eg +h, here's
     *       probably a good place to add them :)
     */

    if (MyConnect(acptr) && (!feature_bool(FEAT_HIS_WHOIS_IDLETIME) ||
                             (sptr == acptr || IsAnOper(sptr) || parc >= 3)))
       send_reply(sptr, RPL_WHOISIDLE, name, CurrentTime - user->last,
                  cli_firsttime(acptr));
  }
}
Esempio n. 15
0
void relay_private_message(struct Client *cptr, struct Client* sptr, const char* name, const char* text)
{
  struct Client* acptr;
  static char *pmsg = NULL;
  static int last_length = 0;
  int len;

  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);

  if (0 == (acptr = FindUser(name))) {
    send_reply(sptr, ERR_NOSUCHNICK, name);
    return;
  }
  if (IsOnlyreg(acptr) && !IsRegnick(sptr)) {
	  send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr));
	  return;
  }

  if (!IsChannelService(acptr))
  {
    if (check_target_limit(sptr, acptr, cli_name(acptr), 0))
    {
      return;
    }
    if (is_silenced(sptr, acptr))
    {
      send_reply(sptr, ERR_SILENCED, cli_name(acptr));
      return;
    }
  }

  if (MyUser(cptr) && !es_representante(cptr)) {
	if ((len = strlen(text)+1) > last_length)
	{
		if (pmsg)
			free(pmsg);
		pmsg = (char *)MyMalloc(sizeof(char)*len);
		last_length = len;
	}
	strcpy(pmsg, text);
	correct_colors(pmsg);

    if (process_badwords(pmsg, BADWORDS_QUERY) != NULL)
      return;
  }

  /*
   * send away message if user away
   */
  if (cli_user(acptr) && cli_user(acptr)->away)
    send_reply(sptr, RPL_AWAY, cli_name(acptr), cli_user(acptr)->away);
  /*
   * deliver the message
   */
  if (MyUser(acptr))
    add_target(acptr, sptr);

  sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
}
Esempio n. 16
0
void relay_directed_message(struct Client* sptr, char* name, char* server, const char* text)
{
  struct Client* acptr;
  char*          host;

  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);
  assert(0 != server);

  if (0 == (acptr = FindServer(server + 1))) {
    send_reply(sptr, ERR_NOSUCHNICK, name);
    return;
  }
  /*
   * NICK[%host]@server addressed? See if <server> is me first
   */
  if (!IsMe(acptr)) {
    sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text);
    return;
  }
  /*
   * Look for an user whose NICK is equal to <name> and then
   * check if it's hostname matches <host> and if it's a local
   * user.
   */
  *server = '\0';
  if ((host = strchr(name, '%')))
    *host++ = '\0';

  /* As reported by Vampire-, it's possible to brute force finding users
   * by sending a message to each server and see which one succeeded.
   * This means we have to remove error reporting.  Sigh.  Better than
   * removing the ability to send directed messages to client servers 
   * Thanks for the suggestion Vampire=.  -- Isomer 2001-08-28
   * Argh, /ping nick@server, disallow messages to non +k clients :/  I hate
   * this. -- Isomer 2001-09-16
   */
  if (!(acptr = FindUser(name)) || !MyUser(acptr) ||
      (!EmptyString(host) && 0 != match(host, cli_user(acptr)->realhost)) ||
      !IsChannelService(acptr)) {
#if 0
    send_reply(sptr, ERR_NOSUCHNICK, name);
#endif
    return;
  }

  *server = '@';
  if (host)
    *--host = '%';

  if (!IsChannelService(sptr) && is_silenced(sptr, acptr)) {
    send_reply(sptr, ERR_SILENCED, cli_name(acptr));
    return;
  }
  
  if (IsOnlyreg(acptr) && !IsRegnick(sptr))
    send_reply(sptr, RPL_MSGONLYREG, cli_name(acptr));
  else
    sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%s :%s", name, text);
}
Esempio n. 17
0
/*
 * The function that actually prints out the WHO reply for a client found
 */
void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan,
            int fields, char* qrt)
{
  char *p1;
  struct Channel *chptr = repchan;

  static char buf1[512];
  /* NOTE: with current fields list and sizes this _cannot_ overrun, 
     and also the message finally sent shouldn't ever be truncated */

  p1 = buf1;
  buf1[1] = '\0';

  /* If we don't have a channel and we need one... try to find it,
     unless the listing is for a channel service, we already know
     that there are no common channels, thus use PubChannel and not
     SeeChannel */
  if (!chptr && (!fields || (fields & (WHO_FIELD_CHA | WHO_FIELD_FLA))) &&
      !IsChannelService(acptr))
  {
    struct Membership* chan;
    for (chan = cli_user(acptr)->channel; chan && !chptr; chan = chan->next_channel)
      if (PubChannel(chan->channel) &&
          (acptr == sptr || !IsZombie(chan)))
        chptr = chan->channel;
  }

  /* Place the fields one by one in the buffer and send it
     note that fields == NULL means "default query" */

  if (fields & WHO_FIELD_QTY)   /* Query type */
  {
    *(p1++) = ' ';
    if (BadPtr(qrt))
      *(p1++) = '0';
    else
      while ((*qrt) && (*(p1++) = *(qrt++)));
  }

  if (!fields || (fields & WHO_FIELD_CHA))
  {
    char *p2;
    *(p1++) = ' ';
    if ((p2 = (chptr ? chptr->chname : NULL)))
      while ((*p2) && (*(p1++) = *(p2++)));
    else
      *(p1++) = '*';
  }

  if (!fields || (fields & WHO_FIELD_UID))
  {
    char *p2 = cli_user(acptr)->username;
    *(p1++) = ' ';
    while ((*p2) && (*(p1++) = *(p2++)));
  }

  if (fields & WHO_FIELD_NIP)
  {
    const char* p2 = IsHiddenHost(acptr) && (!IsViewip(sptr) && acptr != sptr) ?
      feature_str(FEAT_HIDDEN_IP) :
      ircd_ntoa((const char*) &(cli_ip(acptr)));
    *(p1++) = ' ';
    while ((*p2) && (*(p1++) = *(p2++)));
  }

  if (!fields || (fields & WHO_FIELD_HOS))
  {
	char *p2 = (IsViewip(sptr) || acptr == sptr) ? get_realhost(acptr) : get_virtualhost(acptr);
    *(p1++) = ' ';
    while ((*p2) && (*(p1++) = *(p2++)));
  }

  if (!fields || (fields & WHO_FIELD_SER))
  {
    char *p2;
    int haspriv = IsAnOper(sptr) || es_representante(sptr);
    
    if (IsMe(cli_user(acptr)->server))
    {
      if ((sptr != acptr) && feature_bool(FEAT_HIS_WHO_SERVERNAME) && !haspriv)
        p2 = (char *)feature_str(FEAT_HIS_SERVERNAME);
      else
        p2 = cli_name(&me);
    }
    else
    {
      if (IsHiddenserv(cli_user(acptr)->server) && !haspriv)
        p2 = (char *)feature_str(FEAT_HIS_SERVERNAME);
      else
        p2 = cli_name(cli_user(acptr)->server);
    }
    *(p1++) = ' ';
    while ((*p2) && (*(p1++) = *(p2++)));
  }

  if (!fields || (fields & WHO_FIELD_NIC))
  {
    char *p2 = cli_name(acptr);
    *(p1++) = ' ';
    while ((*p2) && (*(p1++) = *(p2++)));
  }

  if (!fields || (fields & WHO_FIELD_FLA))
  {
    *(p1++) = ' ';
    if (cli_user(acptr)->away)
      *(p1++) = 'G';
    else
      *(p1++) = 'H';
    if ((IsAnOper(acptr) || es_representante(acptr) || IsPreoper(acptr))/* &&
	(HasPriv(acptr, PRIV_DISPLAY) || HasPriv(sptr, PRIV_SEE_OPERS))*/)
      *(p1++) = '*';
    if (fields) {
      /* If you specified flags then we assume you know how to parse
       * multiple channel status flags, as this is currently the only
       * way to know if someone has @'s *and* is +'d.
       */
      if (chptr && is_chan_op(acptr, chptr))
        *(p1++) = '@';
      if (chptr && has_voice(acptr, chptr))
        *(p1++) = '+';
	  if (chptr && is_chan_halfop(acptr, chptr))
		*(p1++) = '%';
	  if (chptr && is_chan_owner(acptr, chptr))
		*(p1++) = '.';
      if (chptr && is_zombie(acptr, chptr))
        *(p1++) = '!';
    }
    else {
      if (chptr && is_chan_op(acptr, chptr))
        *(p1++) = '@';
      else if (chptr && has_voice(acptr, chptr))
        *(p1++) = '+';
      else if (chptr && is_zombie(acptr, chptr))
        *(p1++) = '!';
	  else if (chptr && is_chan_halfop(acptr, chptr))
		*(p1++) = '%';
	  else if (chptr && is_chan_owner(acptr, chptr))
		*(p1++) = '.';
    }
    if (IsDeaf(acptr))
      *(p1++) = 'd';
    if (IsAnOper(sptr))
    {
      if (IsInvisible(acptr))
        *(p1++) = 'i';
      if (SendWallops(acptr))
        *(p1++) = 'w';
      if (SendDebug(acptr))
        *(p1++) = 'g';
    }
    if (HasHiddenHost(acptr))
      *(p1++) = 'x';
    if (IsAnOper(acptr))
      *(p1++) = 'o';
	if (IsPreoper(acptr))
	  *(p1++) = 'p';
	if (IsHelpOp(acptr))
	  *(p1++) = 'h';
	if (IsCoadmin(acptr))
	  *(p1++) = 'a';
	if (IsAdmin(acptr))
	  *(p1++) = 'A';
	if (IsDevel(acptr))
	  *(p1++) = 'D';
	if (IsViewip(acptr))
	  *(p1++) = 'X';
  }

  if (!fields || (fields & WHO_FIELD_DIS))
  {
    *p1++ = ' ';
    if (!fields)
      *p1++ = ':';              /* Place colon here for default reply */
    if (feature_bool(FEAT_HIS_WHO_HOPCOUNT) && !IsAnOper(sptr))
      *p1++ = (sptr == acptr) ? '0' : '3';
    else
      /* three digit hopcount maximum */
      p1 += ircd_snprintf(0, p1, 3, "%d", cli_hopcount(acptr));
  }

  if (fields & WHO_FIELD_IDL)
  {
    *p1++ = ' ';
    if (MyUser(acptr)) {
	    p1 += ircd_snprintf(0, p1, 11, "%d",
				CurrentTime - cli_user(acptr)->last);
    }    
    else {
    	    *p1++ = '0';
    }
  }

  if (!fields || (fields & WHO_FIELD_REN))
  {
    char *p2 = cli_info(acptr);
    *p1++ = ' ';
    if (fields)
      *p1++ = ':';              /* Place colon here for special reply */
    while ((*p2) && (*(p1++) = *(p2++)));
  }

  if (fields & WHO_FIELD_ACC)
  {
    char *p2 = cli_user(acptr)->account;
    *(p1++) = ' ';
    while ((*p2) && (*(p1++) = *(p2++)));
  }

  /* The first char will always be an useless blank and we 
     need to terminate buf1 */
  *p1 = '\0';
  p1 = buf1;
  send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1);
}
Esempio n. 18
0
int
m_mode(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Channel *chptr = 0;
  unsigned int hoflags  = 0;
  struct ModeBuf mbuf;
  struct Membership *member = 0;

  if (parc < 2)
    return need_more_params(sptr, "MODE");

  clean_channelname(parv[1]);

  if (!(chptr = FindChannel(parv[1])))
    return set_user_mode(cptr, sptr, parc, parv);

  ClrFlag(sptr, FLAG_TS8);

  member = find_member_link(chptr, sptr);

  if (parc < 3) {
    char modebuf[MODEBUFLEN];
    char parabuf[MODEBUFLEN];

    *modebuf = *parabuf = '\0';
    modebuf[1] = '\0';
    channel_modes(sptr, modebuf, parabuf, sizeof(parabuf), chptr);
    send_reply(sptr, RPL_CHANNELMODEIS, chptr->chname, modebuf, parabuf);
    send_reply(sptr, RPL_CREATIONTIME, chptr->chname, chptr->creationtime);
    return 0;
  }

  if ((!member && !IsChannelService(sptr)) ||
      (member && !IsChanOp(member) && !IsHalfOp(member))) {
    if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_MODE_LCHAN)) {
      modebuf_init(&mbuf, sptr, cptr, chptr,
		   (MODEBUF_DEST_CHANNEL | /* Send mode to channel */
		    MODEBUF_DEST_HACK4));  /* Send HACK(4) notice */
      mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2,
		 (MODE_PARSE_SET |    /* Set the mode */
		  MODE_PARSE_FORCE), NULL); /* Force it to take */
      return modebuf_flush(&mbuf);
    } else
      mode_parse(0, cptr, sptr, chptr, parc - 2, parv + 2,
		 (member ? MODE_PARSE_NOTOPER : MODE_PARSE_NOTMEMBER), NULL);
    return 0;
  }

  modebuf_init(&mbuf, sptr, cptr, chptr,
	       (MODEBUF_DEST_CHANNEL | /* Send mode to channel */
		MODEBUF_DEST_SERVER)); /* Send mode to servers */

  if (member && IsChanOp(member))
    hoflags = MODE_PARSE_SET; /* set unconditionally */
  else if (member && IsHalfOp(member))
    hoflags = MODE_PARSE_ISHALFOP|MODE_PARSE_SET|MODE_PARSE_NOTOPER; /* allowed to +v */
  else
    hoflags = MODE_PARSE_NOTOPER;
  mode_parse(&mbuf, cptr, sptr, chptr, parc - 2, parv + 2, hoflags, member);
  return modebuf_flush(&mbuf);
}
Esempio n. 19
0
void do_oper(struct Client* cptr, struct Client* sptr, struct ConfItem* aconf)
{
  struct Flags old_mode = cli_flags(sptr);
  char*        modes;
  char*        parv[2];
  char*        join[3];
  char         chan[CHANNELLEN-1];
  char*        ajoinchan;
  char*        ajoinnotice;
  unsigned int snomask = 0;

  parv[0] = cli_name(sptr);
  parv[1] = NULL;

  SetOper(sptr);
  client_set_privs(sptr, aconf);
  ClearOper(sptr);

  snomask = ConfSnoMask(aconf) & SNO_ALL;
  snomask |= aconf->snomask & SNO_ALL;

  ajoinchan = ConfAjoinChan(aconf);
  ajoinnotice = ConfAjoinNotice(aconf);

  if (MyUser(sptr)) {
    SetLocOp(sptr);
    if (HasPriv(sptr, PRIV_PROPAGATE))
    {
      ClearLocOp(sptr);
      SetOper(sptr);
      if (HasPriv(sptr, PRIV_ADMIN))
        SetAdmin(sptr);
      if (!IsHideOper(sptr) && !IsChannelService(sptr) && !IsBot(sptr))
        ++UserStats.opers;
    }
    cli_handler(sptr) = OPER_HANDLER;

    SetFlag(sptr, FLAG_WALLOP);
    SetFlag(sptr, FLAG_SERVNOTICE);
    SetFlag(sptr, FLAG_DEBUG);

    if (snomask)
      set_snomask(sptr, snomask, SNO_ADD);
    else
      set_snomask(sptr, feature_int(FEAT_SNOMASK_OPERDEFAULT), SNO_ADD);
    cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */
    cli_max_recvq(sptr) = 0; /* Get the recvq from the oper's class */
    cli_lag_min(sptr) = -2; /* Get the fake lag minimum from the oper's class */
    cli_lag_factor(sptr) = -2; /* Get the fake lag factor from the oper's class */
    send_umode_out(sptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE));
  } else {
    client_send_privs(&me, sptr, sptr);

    if (HasPriv(sptr, PRIV_PROPAGATE)) {
      modes = (HasPriv(sptr, PRIV_ADMIN) ? "aowsg" : "owsg");
    } else {
      modes = "Owsg";
    }

    sendcmdto_one(&me, CMD_MODE, sptr, "%s %s", cli_name(sptr), modes);
  }

  modes = ConfUmode(aconf);
  if (modes) {
    if (MyUser(sptr)) {
      char *umodev[] = { NULL, NULL, NULL, NULL };
      umodev[1] = cli_name(sptr);
      umodev[2] = modes;
      old_mode = cli_flags(sptr);
      set_user_mode(sptr, sptr, 3, umodev, ALLOWMODES_ANY);
      send_umode(NULL, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE));
      if ((cli_snomask(sptr) != feature_int(FEAT_SNOMASK_OPERDEFAULT)) &&
          HasFlag(sptr, FLAG_SERVNOTICE))
        send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr));
    } else {
      if (snomask)
        sendcmdto_one(&me, CMD_MODE, sptr, "%s %s+s +%d", cli_name(sptr), modes, snomask);
      else
        sendcmdto_one(&me, CMD_MODE, sptr, "%s %s", cli_name(sptr), modes);
    }
  }

  send_reply(sptr, RPL_YOUREOPER);

  if ((feature_int(FEAT_HOST_HIDING_STYLE) == 1) ||
      (feature_int(FEAT_HOST_HIDING_STYLE) == 3))
    hide_hostmask(sptr);

  if (!EmptyString(ajoinchan))
  {
    if (!EmptyString(ajoinnotice))
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, ajoinnotice);

    ircd_strncpy(chan, ajoinchan, CHANNELLEN-1);
    join[0] = cli_name(sptr);
    join[1] = chan;
    join[2] = NULL;
    m_join(sptr, sptr, 2, join);
  }

  if (!EmptyString(aconf->autojoinchan))
  {
    if (!EmptyString(aconf->autojoinnotice))
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, aconf->autojoinnotice);

    ircd_strncpy(chan, aconf->autojoinchan, CHANNELLEN-1);
    join[0] = cli_name(sptr);
    join[1] = chan;
    join[2] = NULL;
    m_join(sptr, sptr, 2, join);
  }

  sendto_opmask_butone_global((MyUser(sptr) ? &me : NULL), SNO_OLDSNO,
     "%s (%s@%s) is now operator (%c)",
     cli_name(sptr), cli_user(sptr)->username, cli_user(sptr)->realhost,
     IsOper(sptr) ? 'O' : 'o');

  if (feature_bool(FEAT_OPERMOTD))
    m_opermotd(sptr, sptr, 1, parv);

  log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#C)", aconf->name, sptr);
}
Esempio n. 20
0
/** Read a 'packet' of data from a connection and process it.  Read in
 * 8k chunks to give a better performance rating (for server
 * connections).  Do some tricky stuff for client connections to make
 * sure they don't do any flooding >:-) -avalon
 * @param cptr Client from which to read data.
 * @param socket_ready If non-zero, more data can be read from the client's socket.
 * @return Positive number on success, zero on connection-fatal failure, negative
 *   if user is killed.
 */
static int read_packet(struct Client *cptr, int socket_ready)
{
  unsigned int dolen = 0;
  unsigned int length = 0;

  if (socket_ready &&
      !(IsUser(cptr) &&
	DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD))) {
#if defined(USE_SSL)
    switch (client_recv(cptr, readbuf, sizeof(readbuf), &length)) {
#else
    switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) {
#endif
    case IO_SUCCESS:
      if (length)
      {
        cli_lasttime(cptr) = CurrentTime;
        ClearPingSent(cptr);
        ClrFlag(cptr, FLAG_NONL);
        if (cli_lasttime(cptr) > cli_since(cptr))
          cli_since(cptr) = cli_lasttime(cptr);
      }
      break;
    case IO_BLOCKED:
      break;
    case IO_FAILURE:
      cli_error(cptr) = errno;
      /* SetFlag(cptr, FLAG_DEADSOCKET); */
      return 0;
    }
  }

  /*
   * For server connections, we process as many as we can without
   * worrying about the time of day or anything :)
   */
  if (length > 0 && IsServer(cptr))
    return server_dopacket(cptr, readbuf, length);
  else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr)))
    return connect_dopacket(cptr, readbuf, length);
  else
  {
    /*
     * Before we even think of parsing what we just read, stick
     * it on the end of the receive queue and do it when its
     * turn comes around.
     */
    if (length > 0 && dbuf_put(cptr, &(cli_recvQ(cptr)), readbuf, length) == 0)
      return exit_client(cptr, cptr, &me, "dbuf_put fail");

    if ((DBufLength(&(cli_recvQ(cptr))) > feature_uint(FEAT_CLIENT_FLOOD))
         && !IsChannelService(cptr))
      return exit_client(cptr, cptr, &me, "Excess Flood");

    while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) &&
           (IsTrusted(cptr) || IsChannelService(cptr) || cli_since(cptr) - CurrentTime < 10))
    {
      dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE);
      /*
       * Devious looking...whats it do ? well..if a client
       * sends a *long* message without any CR or LF, then
       * dbuf_getmsg fails and we pull it out using this
       * loop which just gets the next 512 bytes and then
       * deletes the rest of the buffer contents.
       * -avalon
       */
      if (dolen == 0)
      {
        if (DBufLength(&(cli_recvQ(cptr))) < 510)
          SetFlag(cptr, FLAG_NONL);
        else
        {
          /* More than 512 bytes in the line - drop the input and yell
           * at the client.
           */
          DBufClear(&(cli_recvQ(cptr)));
          send_reply(cptr, ERR_INPUTTOOLONG);
        }
      }
      else if (client_dopacket(cptr, dolen) == CPTR_KILLED)
        return CPTR_KILLED;
      /*
       * If it has become registered as a Server
       * then skip the per-message parsing below.
       */
      if (IsHandshake(cptr) || IsServer(cptr))
      {
        while (-1)
        {
          dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf));
          if (dolen <= 0)
            return 1;
          else if (dolen == 0)
          {
            if (DBufLength(&(cli_recvQ(cptr))) < 510)
              SetFlag(cptr, FLAG_NONL);
            else {
              DBufClear(&(cli_recvQ(cptr)));
              /* send_reply(cptr, ERR_INPUTTOOLONG); */
            }
          }
          else if ((IsServer(cptr) &&
                    server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) ||
                   (!IsServer(cptr) &&
                    connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED))
            return CPTR_KILLED;
        }
      }
    }

    /* If there's still data to process, wait 2 seconds first */
    if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) &&
	!t_onqueue(&(cli_proc(cptr))))
    {
      Debug((DEBUG_LIST, "Adding client process timer for %C", cptr));
      cli_freeflag(cptr) |= FREEFLAG_TIMER;
      timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr),
		TT_RELATIVE, 2);
    }
  }
  return 1;
}

/** Start a connection to another server.
 * @param aconf Connect block data for target server.
 * @param by Client who requested the connection (if any).
 * @return Non-zero on success; zero on failure.
 */
int connect_server(struct ConfItem* aconf, struct Client* by)
{
  struct Client*   cptr = 0;
  assert(0 != aconf);

  if (aconf->dns_pending) {
    sendto_opmask(0, SNO_OLDSNO, "Server %s connect DNS pending",
                  aconf->name);
    return 0;
  }
  Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name,
         ircd_ntoa(&aconf->address.addr)));

  if ((cptr = FindClient(aconf->name))) {
    if (IsServer(cptr) || IsMe(cptr)) {
      sendto_opmask(0, SNO_OLDSNO, "Server %s already present from %s",
                    aconf->name, cli_name(cli_from(cptr)));
      if (by && IsUser(by) && !MyUser(by)) {
        sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present "
                      "from %s", by, aconf->name, cli_name(cli_from(cptr)));
      }
      return 0;
    }
    else if (IsHandshake(cptr) || IsConnecting(cptr)) {
      if (by && IsUser(by)) {
        sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in "
                      "progress", by, cli_name(cptr));
      }
      return 0;
    }
  }
  /*
   * If we don't know the IP# for this host and it is a hostname and
   * not a ip# string, then try and find the appropriate host record.
   */
  if (!irc_in_addr_valid(&aconf->address.addr)
      && !ircd_aton(&aconf->address.addr, aconf->host)) {
    char buf[HOSTLEN + 1];

    host_from_uh(buf, aconf->host, HOSTLEN);
    gethost_byname(buf, connect_dns_callback, aconf);
    aconf->dns_pending = 1;
    return 0;
  }
  cptr = make_client(NULL, STAT_UNKNOWN_SERVER);

  /*
   * Copy these in so we have something for error detection.
   */
  ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN);
  ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN);

  /*
   * Attach config entries to client here rather than in
   * completed_connection. This to avoid null pointer references
   */
  attach_confs_byhost(cptr, aconf->host, CONF_SERVER);

  if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) {
    sendto_opmask(0, SNO_OLDSNO, "Host %s is not enabled for "
                  "connecting: no Connect block", aconf->name);
    if (by && IsUser(by) && !MyUser(by)) {
      sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no "
                    "Connect block", by, aconf->name);
    }
    det_confs_butmask(cptr, 0);
    free_client(cptr);
    return 0;
  }
  /*
   * attempt to connect to the server in the conf line
   */
  if (!connect_inet(aconf, cptr)) {
    if (by && IsUser(by) && !MyUser(by)) {
      sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by,
                    cli_name(cptr));
    }
    det_confs_butmask(cptr, 0);
    free_client(cptr);
    return 0;
  }
  /*
   * NOTE: if we're here we have a valid C:Line and the client should
   * have started the connection and stored the remote address/port and
   * ip address name in itself
   *
   * The socket has been connected or connect is in progress.
   */
  make_server(cptr);
  if (by && IsUser(by)) {
    ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s",
		  NumNick(by));
    assert(0 == cli_serv(cptr)->user);
    cli_serv(cptr)->user = cli_user(by);
    cli_user(by)->refcnt++;
  }
  else {
    *(cli_serv(cptr))->by = '\0';
    /* strcpy(cptr->serv->by, "Auto"); */
  }
  cli_serv(cptr)->up = &me;
  SetConnecting(cptr);

  if (cli_fd(cptr) > HighestFd)
    HighestFd = cli_fd(cptr);

  LocalClientArray[cli_fd(cptr)] = cptr;

  Count_newunknown(UserStats);
  /* Actually we lie, the connect hasn't succeeded yet, but we have a valid
   * cptr, so we register it now.
   * Maybe these two calls should be merged.
   */
  add_client_to_list(cptr);
  hAddClient(cptr);
/*    nextping = CurrentTime; */

  return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ?
    completed_connection(cptr) : 1;
}

/** Find the real hostname for the host running the server (or one which
 * matches the server's name) and its primary IP#.  Hostname is stored
 * in the client structure passed as a pointer.
 */
void init_server_identity(void)
{
  const struct LocalConf* conf = conf_get_local();
  assert(0 != conf);

  ircd_strncpy(cli_name(&me), conf->name, HOSTLEN);
  SetYXXServerName(&me, conf->numeric);
}

/** Process events on a client socket.
 * @param ev Socket event structure that has a struct Connection as
 *   its associated data.
 */
static void client_sock_callback(struct Event* ev)
{
  struct Client* cptr;
  struct Connection* con;
  char *fmt = "%s";
  char *fallback = 0;

  assert(0 != ev_socket(ev));
  assert(0 != s_data(ev_socket(ev)));

  con = (struct Connection*) s_data(ev_socket(ev));

  assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);

  cptr = con_client(con);

  assert(0 == cptr || con == cli_connect(cptr));

  switch (ev_type(ev)) {
  case ET_DESTROY:
    con_freeflag(con) &= ~FREEFLAG_SOCKET;

    if (!con_freeflag(con) && !cptr)
      free_connection(con);
#if defined(USE_SSL)
    ssl_free(ev_socket(ev));
#endif
    break;

  case ET_CONNECT: /* socket connection completed */
    if (!completed_connection(cptr) || IsDead(cptr))
      fallback = cli_info(cptr);
    break;

  case ET_ERROR: /* an error occurred */
    fallback = cli_info(cptr);
    cli_error(cptr) = ev_data(ev);
    /* If the OS told us we have a bad file descriptor, we should
     * record that for future reference.
     */
    if (cli_error(cptr) == EBADF)
      cli_fd(cptr) = -1;
    if (s_state(&(con_socket(con))) == SS_CONNECTING) {
      completed_connection(cptr);
      /* for some reason, the os_get_sockerr() in completed_connection()
       * can return 0 even when ev_data(ev) indicates a real error, so
       * re-assign the client error here.
       */
      cli_error(cptr) = ev_data(ev);
      break;
    }
    /*FALLTHROUGH*/
  case ET_EOF: /* end of file on socket */
    Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d", cli_fd(cptr),
	   cli_error(cptr)));
    SetFlag(cptr, FLAG_DEADSOCKET);
    if ((IsServer(cptr) || IsHandshake(cptr)) && cli_error(cptr) == 0) {
      exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)",
		      cli_name(cptr), cli_serv(cptr)->last_error_msg);
      return;
    } else {
      fmt = "Read error: %s";
      fallback = "EOF from client";
    }
    break;

  case ET_WRITE: /* socket is writable */
    ClrFlag(cptr, FLAG_BLOCKED);
    if (cli_listing(cptr) && MsgQLength(&(cli_sendQ(cptr))) < 2048)
      list_next_channels(cptr);
    Debug((DEBUG_SEND, "Sending queued data to %C", cptr));
    send_queued(cptr);
    break;

  case ET_READ: /* socket is readable */
    if (!IsDead(cptr)) {
      Debug((DEBUG_DEBUG, "Reading data from %C", cptr));
      if (read_packet(cptr, 1) == 0) /* error while reading packet */
	fallback = "EOF from client";
    }
    break;

  default:
    assert(0 && "Unrecognized socket event in client_sock_callback()");
    break;
  }

  assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr));

  if (fallback) {
    const char* msg = (cli_error(cptr)) ? strerror(cli_error(cptr)) : fallback;
    if (!msg)
      msg = "Unknown error";
    exit_client_msg(cptr, cptr, &me, fmt, msg);
  }
}

/** Process a timer on client socket.
 * @param ev Timer event that has a struct Connection as its
 * associated data.
 */
static void client_timer_callback(struct Event* ev)
{
  struct Client* cptr;
  struct Connection* con;

  assert(0 != ev_timer(ev));
  assert(0 != t_data(ev_timer(ev)));
  assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev));

  con = (struct Connection*) t_data(ev_timer(ev));

  assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY);

  cptr = con_client(con);

  assert(0 == cptr || con == cli_connect(cptr));

  if (ev_type(ev)== ET_DESTROY) {
    con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */

    if (!con_freeflag(con) && !cptr)
      free_connection(con); /* client is being destroyed */
  } else {
    Debug((DEBUG_LIST, "Client process timer for %C expired; processing",
	   cptr));
    read_packet(cptr, 0); /* read_packet will re-add timer if needed */
  }

  assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr));
}
/*
 * 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;
}
Esempio n. 22
0
/* Rewritten by Run - 24 sept 94 */
static void exit_one_client(struct Client* bcptr, const char* comment)
{
  struct SLink *lp;
  struct Ban *bp;

  if (cli_serv(bcptr) && cli_serv(bcptr)->client_list)  /* Was SetServerYXX called ? */
    ClearServerYXX(bcptr);      /* Removes server from server_list[] */
  if (IsUser(bcptr)) {
    /*
     * clear out uping requests
     */
    if (IsUPing(bcptr))
      uping_cancel(bcptr, 0);
    /*
     * Stop a running /LIST clean
     */
    if (MyUser(bcptr) && cli_listing(bcptr)) {
      MyFree(cli_listing(bcptr));
      cli_listing(bcptr) = NULL;
    }
    /*
     * If a person is on a channel, send a QUIT notice
     * to every client (person) on the same channel (so
     * that the client can show the "**signoff" message).
     * (Note: The notice is to the local clients *only*)
     */
    sendcmdto_common_channels_butone(bcptr, CMD_QUIT, NULL, ":%s", comment);

    remove_user_from_all_channels(bcptr);

    /* Clean up invitefield */
    while ((lp = cli_user(bcptr)->invited))
      del_invite(bcptr, lp->value.chptr);

    /* Clean up silencefield */
    while ((bp = cli_user(bcptr)->silence)) {
      cli_user(bcptr)->silence = bp->next;
      free_ban(bp);
    }

    /* Clean up smarks field */
    del_marks(bcptr);

    /* Clean up watch lists */
    if (MyUser(bcptr))
      del_list_watch(bcptr);
    /* Notify Logout */
    check_status_watch(bcptr, RPL_LOGOFF);

    /* Clean up snotice lists */
    if (MyUser(bcptr))
      set_snomask(bcptr, ~0, SNO_DEL);

    if (IsInvisible(bcptr)) {
      assert(UserStats.inv_clients > 0);
      --UserStats.inv_clients;
    }
    if (IsOper(bcptr) && !IsHideOper(bcptr) && !IsChannelService(bcptr) && !IsBot(bcptr)) {
      assert(UserStats.opers > 0);
      --UserStats.opers;
    }
    if (MyConnect(bcptr))
      Count_clientdisconnects(bcptr, UserStats);
    else
      Count_remoteclientquits(UserStats, bcptr);
  }
  else if (IsServer(bcptr))
  {
    /* Remove downlink list node of uplink */
    remove_dlink(&(cli_serv(cli_serv(bcptr)->up))->down, cli_serv(bcptr)->updown);
    cli_serv(bcptr)->updown = 0;

    if (MyConnect(bcptr))
      Count_serverdisconnects(UserStats);
    else
      Count_remoteserverquits(UserStats);
  }
  else if (IsMe(bcptr))
  {
    sendto_opmask_butone(0, SNO_OLDSNO, "ERROR: tried to exit me! : %s",
			 comment);
    return;                     /* ...must *never* exit self! */
  }
  else if (IsUnknown(bcptr) || IsConnecting(bcptr) || IsHandshake(bcptr))
    Count_unknowndisconnects(UserStats);

  /*
   * Update IPregistry
   */
  if (IsIPChecked(bcptr))
    IPcheck_disconnect(bcptr);

  /* 
   * Remove from serv->client_list
   * NOTE: user is *always* NULL if this is a server
   */
  if (cli_user(bcptr)) {
    assert(!IsServer(bcptr));
    /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
    RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr));
  }

  /* Remove bcptr from the client list */
#ifdef DEBUGMODE
  if (hRemClient(bcptr) != 0)
    Debug((DEBUG_ERROR, "%p !in tab %s[%s] %p %p %p %d %d %p",
          bcptr, cli_name(bcptr), cli_from(bcptr) ? cli_sockhost(cli_from(bcptr)) : "??host",
          cli_from(bcptr), cli_next(bcptr), cli_prev(bcptr), cli_fd(bcptr),
          cli_status(bcptr), cli_user(bcptr)));
#else
  hRemClient(bcptr);
#endif
  remove_client_from_list(bcptr);
}