Beispiel #1
0
int ms_set(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
    struct Client *acptr;
    if (parc < 4)
        return need_more_params(sptr, "SET");
    if (!(acptr = FindNServer(parv[1])))
        return 0;
    if (!IsMe(acptr)) {
        sendcmdto_serv_butone(sptr, CMD_SET, cptr, "%C %s :%s",
                              acptr, parv[2], parv[3]);
        return 0;
    }
    return feature_set(sptr, (const char* const*)parv + 2, parc - 2);
}
Beispiel #2
0
/** Forward a Z-line to other servers.
 * @param[in] cptr Client that sent us the Z-line.
 * @param[in] sptr Client that originated the Z-line.
 * @param[in] zline Z-line to forward.
 * @return Zero.
 */
static int
zline_propagate(struct Client *cptr, struct Client *sptr, struct Zline *zline)
{
  if (ZlineIsLocal(zline))
    return 0;

  assert(zline->zl_lastmod);

  sendcmdto_serv_butone(sptr, CMD_ZLINE, cptr, "* %c%s %Tu %Tu %Tu :%s",
			ZlineIsRemActive(zline) ? '+' : '-',
			zline->zl_mask, zline->zl_expire - TStime(),
			zline->zl_lastmod, zline->zl_lifetime, zline->zl_reason);

  return 0;
}
Beispiel #3
0
/*
 * ms_oper - server message handler
 */
int ms_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  assert(0 != cptr);
  assert(IsServer(cptr));
  /*
   * if message arrived from server, trust it, and set to oper
   */
  if (!IsServer(sptr) && !IsOper(sptr))
  {
    ++UserStats.opers;
    SetFlag(sptr, FLAG_OPER);
    sendcmdto_serv_butone(sptr, CMD_MODE, cptr, "%s :+o", parv[0]);
  }
  return 0;
}
Beispiel #4
0
/** Execute expired channel destruction events.
 * @param[in] ev Expired timer event (ignored).
 */
void exec_expired_destruct_events(struct Event* ev)
{
  int i = 0;
  struct DestructEvent** list_bottom;
  for(list_bottom = &minute_list_bottom; i < 2; ++i, list_bottom = &days_list_bottom)
  {
    while (*list_bottom && TStime() >= (*list_bottom)->expires)
    {
      struct Channel* chptr = (*list_bottom)->chptr;
      /* Send DESTRUCT message */
      sendcmdto_serv_butone(&me, CMD_DESTRUCT, 0, "%s %Tu", chptr->chname, chptr->creationtime);
      remove_destruct_event(chptr);
      destruct_channel(chptr);
    }
  }
}
/*
 * ms_account - server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = numeric of client to act on
 * parv[2] = account name (12 characters or less)
 */
int ms_account(struct Client* cptr, struct Client* sptr, int parc,
	       char* parv[])
{
  struct Client *acptr;
  int hidden;

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

  if (!IsServer(sptr))
    return protocol_violation(cptr, "ACCOUNT from non-server %s",
			      cli_name(sptr));

  if (!(acptr = findNUser(parv[1])))
    return 0; /* Ignore ACCOUNT for a user that QUIT; probably crossed */

  if (IsAccount(acptr))
    return protocol_violation(cptr, "ACCOUNT for already registered user %s "
			      "(%s -> %s)", cli_name(acptr),
			      cli_user(acptr)->account, parv[2]);

  assert(0 == cli_user(acptr)->account[0]);

  if (strlen(parv[2]) > ACCOUNTLEN) {
    return protocol_violation(cptr, "Received account (%s) longer than %d for %s; ignoring.", parv[2], ACCOUNTLEN, cli_name(acptr));
  }

  if (parc > 3) {
    cli_user(acptr)->acc_create = atoi(parv[3]);
    Debug((DEBUG_DEBUG, "Received timestamped account: account \"%s\", "
	   "timestamp %Tu", parv[2], cli_user(acptr)->acc_create));
  }

  hidden = HasHiddenHost(acptr);
  SetAccount(acptr);
  ircd_strncpy(cli_user(acptr)->account, parv[2], ACCOUNTLEN);
  if (!hidden)
    hide_hostmask(acptr);

  sendcmdto_serv_butone(sptr, CMD_ACCOUNT, cptr,
			cli_user(acptr)->acc_create ? "%C %s %Tu" : "%C %s",
			acptr, cli_user(acptr)->account,
			cli_user(acptr)->acc_create);

  return 0;
}
Beispiel #6
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;
}
Beispiel #7
0
/*
 * ms_svsinfo - server message handler
 *
 *   parv[0] = Sender prefix
 *   parv[1] = Numeric nick
 *   parv[parc-1] = New info line
 */
int ms_svsinfo(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client *acptr;

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

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

  if (ircd_strcmp(acptr->cli_info, parv[parc-1]) == 0)
    return 0; /* New info already the same as current one */

  /* Set the info line, if the length is over REALLEN then it will
     be truncated */
  ircd_strncpy(acptr->cli_info, parv[2], REALLEN);


  sendcmdto_serv_butone(sptr, CMD_SVSINFO, cptr, "%C :%s", acptr, acptr->cli_info);

  return 0;
}
/*
 * ms_destruct - server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = channel channelname
 * parv[2] = channel time stamp
 *
 * This function does nothing, it does passes DESTRUCT to the other servers.
 * In the future we will start to use this message.
 *
 */
int ms_destruct(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  time_t chanTS;                /* Creation time of the channel */

  assert(0 != cptr);
  assert(0 != sptr);
  assert(IsServer(cptr));

  if (parc < 3 || EmptyString(parv[2]))
    return need_more_params(sptr,"DESTRUCT");

  /* Don't pass on DESTRUCT messages for channels that exist */
  if (FindChannel(parv[1]))
    return 0;

  chanTS = atoi(parv[2]);

  /* Pass on DESTRUCT message */
  sendcmdto_serv_butone(sptr, CMD_DESTRUCT, cptr, "%s %Tu", parv[1], chanTS);

  return 0;
}
Beispiel #9
0
/*
 *  ms_svsnoop
 *
 *  parv[0] = sender prefix
 *  parv[1] = server
 *  parv[2] = +/-
 *
 *  Ported From Ultimate IRCd
 */
int ms_svsnoop(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct ConfItem *aconf;
  struct Client *server = 0;
  char           c;
  char*          cp;

  if (!IsServer(sptr) || parc < 3)
    return 0;

  /* this could be done with hunt_server_cmd but its a bucket of shit */
  if (!string_has_wildcards(parv[1]))
    server = FindServer(parv[1]);
  else
    server = find_match_server(parv[1]);

  if (!server)
    return 0;

  if (server == &me) {
    cp = parv[2];
    c = *cp;
    if (c == '+') {
      for(aconf = GlobalConfList; aconf; aconf = aconf->next) {
        if (aconf->status & CONF_OPERATOR)
          aconf->status |= CONF_ILLEGAL;
      }
      SetServerNoop(&me);
    } else {
      rehash (&me, 2);
      ClearServerNoop(&me);
    }
  }

  sendcmdto_serv_butone(sptr, CMD_SVSNOOP, cptr, "%s %s", parv[1], parv[2]);
  return 0;
}
/*
 * m_fakehost - fakehost user message handler
 *
 * parv[0] = sender prefix
 * parv[1] = new fake host
 */
int m_fakehost(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  if (!HasPriv(sptr, PRIV_SET_FAKEHOST))
    return send_reply(sptr, ERR_NOPRIVILEGES);

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

  /* Ignore the assignment if it changes nothing */
  if (HasFakeHost(cptr) &&
      ircd_strcmp(cli_user(cptr)->fakehost, parv[1]) == 0)
  {
    return 0;
  }

  /* Assign and propagate the fakehost */
  SetFakeHost(cptr);
  ircd_strncpy(cli_user(cptr)->fakehost, parv[1], HOSTLEN);
  hide_hostmask(cptr);

  sendcmdto_serv_butone(sptr, CMD_FAKEHOST, cptr, "%C %s", sptr,
                        cli_user(cptr)->fakehost);
  return 0;
}
/*
 * ms_nick - server message handler for nicks
 * parv[0] = sender prefix
 * parv[1] = nickname
 *
 * If from server, source is client:
 *   parv[2] = timestamp
 *
 * Source is server:
 *   parv[2] = hopcount
 *   parv[3] = timestamp
 *   parv[4] = username
 *   parv[5] = hostname
 *   parv[6] = umode (optional)
 *   parv[parc-3] = IP#                 <- Only Protocol >= 10
 *   parv[parc-2] = YXX, numeric nick   <- Only Protocol >= 10
 *   parv[parc-1] = info
 *   parv[0] = server
 */
int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client* acptr;
  char           nick[NICKLEN + 2];
  time_t         lastnick = 0;
  int            differ = 1;
  int            samelastnick = 0;
  
  assert(0 != cptr);
  assert(0 != sptr);
  assert(IsServer(cptr));

  if ((IsServer(sptr) && parc < 8) || parc < 3) {
    sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C",
			 parv[1], cptr);
    return need_more_params(sptr, "NICK");
  }

  ircd_strncpy(nick, parv[1], NICKLEN);
  nick[NICKLEN] = '\0';

  if (IsServer(sptr)) {
    lastnick = atoi(parv[3]);
    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) 
      cli_serv(sptr)->lag = TStime() - lastnick;
  }
  else {
    lastnick = atoi(parv[2]); 
    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr))
      cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick;
  }
  /*
   * If do_nick_name() returns a null name OR if the server sent a nick
   * name and do_nick_name() changed it in some way (due to rules of nick
   * creation) then reject it. If from a server and we reject it,
   * and KILL it. -avalon 4/4/92
   */
  if (0 == do_nick_name(nick) || 0 != strcmp(nick, parv[1])) {
    send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]);

    ++ServerStats->is_kill;
    sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1],
			 parv[0], cptr);
    sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])",
		  IsServer(sptr) ? parv[parc - 2] : parv[0], 
		  cli_name(&me), parv[1], nick, cli_name(cptr));
    if (!IsServer(sptr)) {
      /*
       * bad nick _change_
       */
      sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)",
			    parv[0], cli_name(&me), cli_name(cptr), parv[0],
			    cli_user(sptr) ? cli_username(sptr) : "",
			    cli_user(sptr) ? cli_name(cli_user(sptr)->server) :
			    cli_name(cptr));
    }
    return 0;
  }
  /*
   * Check against nick name collisions.
   *
   * Put this 'if' here so that the nesting goes nicely on the screen :)
   * We check against server name list before determining if the nickname
   * is present in the nicklist (due to the way the below for loop is
   * constructed). -avalon
   */
   
  assert(NULL == strchr(nick,'.'));

  acptr = FindClient(nick);
  if (!acptr) {
    /*
     * No collisions, all clear...
     */
    return set_nick_name(cptr, sptr, nick, parc, parv);
  }
  assert(0 != acptr);

  /*
   * If acptr == sptr, then we have a client doing a nick
   * change between *equivalent* nicknames as far as server
   * is concerned (user is changing the case of his/her
   * nickname or somesuch)
   */
  if (acptr == sptr) {
    if (strcmp(cli_name(acptr), nick) == 0)
      /*
       * This is just ':old NICK old' type thing.
       * Just forget the whole thing here. There is
       * no point forwarding it to anywhere,
       * especially since servers prior to this
       * version would treat it as nick collision.
       */
      return 0;                        /* NICK Message ignored */
    else
      /*
       * Allows change of case in his/her nick
       */
      return set_nick_name(cptr, sptr, nick, parc, parv);
  }

  /*
   * Note: From this point forward it can be assumed that
   * acptr != sptr (point to different client structures).
   */
  assert(acptr != sptr);
  /*
   * If the older one is "non-person", the new entry is just
   * allowed to overwrite it. Just silently drop non-person,
   * and proceed with the nick. This should take care of the
   * "dormant nick" way of generating collisions...
   */
  if (IsUnknown(acptr) && MyConnect(acptr)) {
    ++ServerStats->is_ref;
    IPcheck_connect_fail(cli_ip(acptr));
    exit_client(cptr, acptr, &me, "Overridden by other sign on");
    return set_nick_name(cptr, sptr, nick, parc, parv);
  }
  /*
   * Decide, we really have a nick collision and deal with it
   */
  /*
   * NICK was coming from a server connection.
   * This means we have a race condition (two users signing on
   * at the same time), or two net fragments reconnecting with the same nick.
   * The latter can happen because two different users connected
   * or because one and the same user switched server during a net break.
   * If the TimeStamps are equal, we kill both (or only 'new'
   * if it was a ":server NICK new ...").
   * Otherwise we kill the youngest when user@host differ,
   * or the oldest when they are the same.
   * We treat user and ~user as different, because if it wasn't
   * a faked ~user the AUTH wouldn't have added the '~'.
   * --Run
   *
   */


  if (IsServer(sptr)) {
    /*
     * A new NICK being introduced by a neighbouring
     * server (e.g. message type ":server NICK new ..." received)
     *
     * compare IP address and username
     */
    differ =  (cli_ip(acptr).s_addr != htonl(base64toint(parv[parc - 3]))) ||
              (0 != ircd_strcmp(cli_user(acptr)->username, parv[4]));
    sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- "
			 "%C %Tu (%s user@host))", acptr, cli_from(acptr),
			 cli_lastnick(acptr), cptr, lastnick,
			 differ ? "Different" : "Same");
  }
  else {
    /*
     * A NICK change has collided (e.g. message type ":old NICK new").
     *
     * compare IP address and username
     */
    differ =  (cli_ip(acptr).s_addr != cli_ip(sptr).s_addr) ||
              (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username));              
    sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to "
			 "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr),
			 cli_lastnick(acptr), cptr, lastnick);
  }
  /*
   * Now remove (kill) the nick on our side if it is the youngest.
   * If no timestamp was received, we ignore the incoming nick
   * (and expect a KILL for our legit nick soon ):
   * When the timestamps are equal we kill both nicks. --Run
   * acptr->from != cptr should *always* be true (?).
   *
   * This exits the client sending the NICK message
   */
  if (cli_from(acptr) != cptr) {
    if ((differ && lastnick >= cli_lastnick(acptr)) ||
	(!differ && lastnick <= cli_lastnick(acptr))) {
      if (!IsServer(sptr)) {
        ++ServerStats->is_kill;
	sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (Nick collision)",
			      sptr, cli_name(&me));
        assert(!MyConnect(sptr));

        SetFlag(sptr, FLAG_KILLED);

	exit_client_msg(cptr, sptr, &me,
			       "Killed (%s (Nick collision))",
			       feature_str(FEAT_HIS_SERVERNAME));

	sptr = 0; /* Make sure we don't use the dead client */

      } else {
        /* We need to kill this incoming client, which hasn't been properly registered yet.
         * Send a KILL message upstream to the server it came from  */
        sendcmdto_one(&me, CMD_KILL, sptr, "%s :%s (Nick collision)", parv[parc-2], cli_name(&me));
      } 
      /* If the two have the same TS then we want to kill both sides, so
       * don't leave yet!
       */
      if (lastnick != cli_lastnick(acptr))
        return 0;                /* Ignore the NICK */
    }
    send_reply(acptr, ERR_NICKCOLLISION, nick);
  }

  ++ServerStats->is_kill;
  SetFlag(acptr, FLAG_KILLED);
  
  if (lastnick == cli_lastnick(acptr))
    samelastnick = 1;
    
  /*
   * This exits the client we had before getting the NICK message
   */
  if (differ) {
    sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (older nick "
			  "overruled)", acptr, cli_name(&me));
    if (MyConnect(acptr)) {
      sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (older "
		    "nick overruled))",  feature_str(FEAT_HIS_SERVERNAME));
      sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick "
		    "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME));
    }

    exit_client_msg(cptr, acptr, &me, "Killed (%s (older nick "
		    "overruled))", feature_str(FEAT_HIS_SERVERNAME));
  }
  else {
    sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (nick collision from "
			  "same user@host)", acptr, cli_name(&me));
    if (MyConnect(acptr)) {
      sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (%s (nick "
		    "collision from same user@host))",
		    feature_str(FEAT_HIS_SERVERNAME));
      sendcmdto_one(&me, CMD_KILL, acptr, "%C :%s (older nick "
		    "overruled)", acptr, feature_str(FEAT_HIS_SERVERNAME));
    }
    exit_client_msg(cptr, acptr, &me, "Killed (%s (nick collision from "
		    "same user@host))", feature_str(FEAT_HIS_SERVERNAME));
  }
  if (samelastnick)
    return 0;

  assert(0 != sptr);
  return set_nick_name(cptr, sptr, nick, parc, parv);
}
/*
 * 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_user(who)->server)
    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
   */
  if (!IsServer(sptr) && member && cli_from(who) != cptr &&
      (!(sptr_link = find_member_link(chptr, sptr)) || !IsChanOp(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) || 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, OpLevel(member));
      if (HasVoice(member))
	modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, who, MAXOPLEVEL + 1);

      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 */
      if (IsDelayedJoin(member)) {
        if (MyUser(who))
          sendcmdto_one(IsServer(sptr) ? &his : sptr, CMD_KICK,
                        who, "%H %C :%s", chptr, who, comment);
      } else {
        sendcmdto_channel_butserv_butone(IsServer(sptr) ? &his : sptr, CMD_KICK,
                                         chptr, NULL, 0, "%H %C :%s", chptr, who,
                                         comment);
      }

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

  return 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;
}
Beispiel #14
0
/*
 * ms_svsnick - server message handler
 * parv[0] = sender prefix
 * parv[1] = Target numeric
 * parv[2] = New nickname
 */
int ms_svsnick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client* acptr = NULL;
  struct Client* acptr2 = NULL;
  char		 nick[NICKLEN + 2];
  char*		 arg;

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

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

  if (ircd_strcmp(cli_name(acptr), parv[2]) == 0)
    return 0; /* Nick already set to what SVSNICK wants, ignoring... */

  /*
   * Basic sanity checks
   */

  /*
   * Don't let them make us send back a really long string of
   * garbage
   */
  arg = parv[2];
  if (strlen(arg) > IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN)))
    arg[IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))] = '\0';

  strcpy(nick, arg);

  /*
   * If do_nick_name() returns a null name then reject it.
   */
  if (0 == do_nick_name(nick))
    return 0;

  /*
   * Check if this is a LOCAL user trying to use a reserved (Juped)
   * nick, if so tell him that it's a nick in use...
   */
  if (isNickJuped(nick))
    return 0;                        /* NICK message ignored */

  if (feature_bool(FEAT_OPER_SINGLELETTERNICK) && !IsAnOper(acptr)
      && nick[1] == '\0')
    return 0;

  /*
   * Set acptr2 to the client pointer of any user with nick's name.
   * If the user is the same as the person being svsnick'ed, let it
   * through as it is probably a change in the nickname's case.
   */
  if ((acptr2 = FindClient(nick))) {
    /*
     * If acptr == acptr2, then we have a client doing a nick
     * change between *equivalent* nicknames as far as server
     * is concerned (user is changing the case of his/her
     * nickname or somesuch), so we let it through :)
     */
    if (acptr != acptr2) {
      /* Nick collision occured, kill user with specific reason */
      ++ServerStats->is_kill;
      /* ??? - Why do we use cptr here? */
      SetFlag(cptr, FLAG_KILLED);
      exit_client(cptr, acptr2, &me, "Killed (Nickname Enforcement)");
    }
  }

  set_nick_name(acptr, acptr, nick, parc, parv, 1);
  sendcmdto_serv_butone(sptr, CMD_SVSNICK, cptr, "%s %s", parv[1], nick);
  return 0;
}
Beispiel #15
0
/*
 * m_notice - generic message handler
 */
int m_notice(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  char*           name;
  char*           server;
  int             ret = 0;
  int             i;
  int             j;
  int             fd = 0;
  int             count;
  char            *clean;
  char*           vector[MAXTARGETS];
  char*           temp; /* added by Vadtec 02/25/2008 */
  char*           parv_temp; /* added by Vadtec 02/26/2008 */
  int             found_g = 0; /* added by Vadtec 02/26/2008 */
  int             sent = 0; /* added by Vadtec 03/13/2008 */
  struct Client*  acptr; /* added by Vadtec 02/26/2008 */
  struct Channel* chptr; /* added by Vadtec 02/27/2008 */
  int             isdcc = 0;

  assert(0 != cptr);
  assert(cptr == sptr);

  ClrFlag(sptr, FLAG_TS8);

  if (parc < 2 || EmptyString(parv[1]))
    return send_reply(sptr, ERR_NORECIPIENT, MSG_NOTICE);

  if (parc < 3 || EmptyString(parv[parc - 1]))
    return send_reply(sptr, ERR_NOTEXTTOSEND);

  if (parv[1][0] == '@' && IsChannelPrefix(parv[1][1])) {
    parv[1]++;                        /* Get rid of '@' */
    return m_wallchops(cptr, sptr, parc, parv);
  }

  count = unique_name_vector(parv[1], ',', vector, MAXTARGETS);

  /* Check here to make sure that the client is ours so we dont respond to NOTICES from other server's users. - Vadtec 02/25/2008 */
  /* Also, check to make sure that the notice is actually destined for the *server* and not another user. That way we don't process
     some user saying "what version do you use" to another user via notice. - Vadtec 03/13/2008 */
  if (feature_bool(FEAT_CTCP_VERSIONING) && MyConnect(sptr) && !strcmp(parv[1], cli_name(&me))) {
    /*
     Added by Vadtec 02/25/2008.
     This is so that we can do version checking (and banning) of connecting clients.
     Rules: Only one really. CTCP VERSION is not part of the RFC and therefore clients are not required to respond to
     a request for their version.
     NOTE: If we are lucky enough to have _GNU_SOURCE, we will use it over the standard strstr because its case insensetive.
           This should help against clients that like to send lower case CTCPs from slipping through as easily with only one
           function call.
    */
    for (fd = HighestFd; fd >= 0 && !sent; --fd) { /* Added the "sent" check here so that if we have already sent the notice
                                                      we don't needlessly loop through *all* the users - Vadtec 03/13/2008 */
      if ((acptr = LocalClientArray[fd])) {
        if (!cli_user(acptr))
          continue;

        #ifdef _GNU_SOURCE
        if ((temp = strcasestr(parv[2], "\x01VERSION"))) { /* added \x01 to the string so that it will *only* respond to CTCP version
                                                              replies. Seems redundant, but we dont want the users doing
                                                              /notice <server> version (and getting away with it) - Vadtec 03/13/2008 */
          temp = strchrnul(parv[2], ' '); /* Moved this here to take advantage of strchrnul - added by Vadtec 03/13/2008 */
        #else
        if ((temp = strstr(parv[2], "\x01VERSION")) || (temp = strstr(parv[2], "\x01version"))) { /* See above comment about \x01 - Vadtec */
          temp = strchr(parv[2], ' '); /* Moved this here to take advantage of strchrnul - added by Vadtec 03/13/2008 */
          if (temp == 0)
            temp = parv[2] + strlen(parv[2]); /* This does the same thing as strchrnul - Vadtec */
        #endif
          parv_temp = parv[2];
          j = 0;
          while (j <= (temp - parv[2])) { parv_temp++; j++; }

          clean = normalizeBuffer(parv_temp);
          doCleanBuffer((char *) clean);

          ircd_strncpy(cli_version(sptr), normalizeBuffer(clean), VERSIONLEN);
          sendcmdto_serv_butone(&me, CMD_MARK, cptr, "%s %s :%s", cli_name(sptr), MARK_CVERSION, cli_version(sptr));

          /* Moved here to solve duplicate MARK's if any of the CTCP_* conditions were false 05/13/2009 */
          sent = 1;

          if (feature_bool(FEAT_CTCP_VERSIONING_CHAN)) {
            sprintf(temp, "%s has version \002%s\002", cli_name(sptr), cli_version(sptr));
            /* Announce to channel. */
            if ((chptr = FindChannel(feature_str(FEAT_CTCP_VERSIONING_CHANNAME)))) {
              if (feature_bool(FEAT_CTCP_VERSIONING_USEMSG))
                sendcmdto_channel_butone(&me, CMD_PRIVATE, chptr, cptr, SKIP_DEAF | SKIP_BURST, '\0', "%H :%s", chptr, temp);
              else
                sendcmdto_channel_butone(&me, CMD_NOTICE, chptr, cptr, SKIP_DEAF | SKIP_BURST, '\0', "%H :%s", chptr, temp);
              /* Removed sent=1 from here because it caused the MARK above to be sent
                 more then once if any of the conditions leading here are false 05/13/2009 */
            }
          }

          if (feature_bool(FEAT_CTCP_VERSIONING_KILL)) {
            if ((found_g = find_kill(acptr))) {
              sendto_opmask_butone(0, found_g == -2 ? SNO_GLINE : SNO_OPERKILL,
                                   found_g == -2 ? "G-line active for %s%s" :
                                   "K-line active for %s%s",
                                   IsUnknown(sptr) ? "Unregistered Client ":"",
                                   get_client_name(sptr, SHOW_IP));
              return exit_client_msg(cptr, acptr, &me, "Banned Client: %s", cli_version(acptr));
            }
          }
          else
            return 0;
        }
      }
    }
  }

  for (i = 0; i < count; ++i) {
    name = vector[i];
    if (IsChannelPrefix(*name)) {
      ret = find_fline(cptr, sptr, parv[parc-1], WFFLAG_CHANNOTICE, name);
      if (ret != 0) {
        if (ret == 2)
          return CPTR_KILLED;
        else
          return 0;
      }
    } else {
      #ifdef _GNU_SOURCE
      if ((temp = strcasestr(parv[2], "\001DCC"))) {
        temp = strchrnul(parv[2], ' ');
      #else
      if ((temp = strstr(parv[2], "\001DCC")) || (temp = strstr(parv[2], "\001dcc"))) {
        temp = strchr(parv[2], ' ');
      #endif
        isdcc = 1;
        ret = find_fline(cptr, sptr, parv[parc-1], WFFLAG_DCC, name);
        if (ret != 0) {
          if (ret == 2)
            return CPTR_KILLED;
          else
            return 0;
        }
      }

      if (!isdcc) {
        ret = find_fline(cptr, sptr, parv[parc-1], WFFLAG_NOTICE, name);
        if (ret != 0) {
          if (ret == 2)
            return CPTR_KILLED;
          else
            return 0;
        }
      }
    }
  }
  i = 0;

  for (i = 0; i < count; ++i) {
    name = vector[i];
    /*
     * channel msg?
     */
    if (IsChannelPrefix(*name)) {
      relay_channel_notice(sptr, name, parv[parc - 1], count);
    }
    /*
     * we have to check for the '@' at least once no matter what we do
     * handle it first so we don't have to do it twice
     */
    else if ((server = strchr(name, '@')))
      relay_directed_notice(sptr, name, server, parv[parc - 1]);
    else 
      relay_private_notice(sptr, name, parv[parc - 1]);
  }
  return 0;
}

/*
 * ms_notice - server message handler
 */
int ms_notice(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  char* name;
  char* server;

  ClrFlag(sptr, FLAG_TS8);

  if (parc < 3) {
    /*
     * we can't deliver it, sending an error back is pointless
     */
    return protocol_violation(sptr,"Not enough params for NOTICE");
  }
  name = parv[1];
  /*
   * channel msg?
   */
  if (IsChannelPrefix(*name)) {
    server_relay_channel_notice(sptr, name, parv[parc - 1]);
  }
  /*
   * coming from another server, we have to check this here
   */
  else if ('$' == *name && IsOper(sptr)) {
    server_relay_masked_notice(sptr, name, parv[parc - 1]);
  }
  else if ((server = strchr(name, '@'))) {
    /*
     * XXX - can't get away with not doing everything
     * relay_directed_notice has to do
     */
    relay_directed_notice(sptr, name, server, parv[parc - 1]);
  }
  else {
    server_relay_private_notice(sptr, name, parv[parc - 1]);
  }
  return 0;
}

/*
 * mo_notice - oper message handler
 */
int mo_notice(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  char*           name;
  char*           server;
  int             i;
  int             count;
  char*           vector[MAXTARGETS];
  assert(0 != cptr);
  assert(cptr == sptr);

  ClrFlag(sptr, FLAG_TS8);

  if (parc < 2 || EmptyString(parv[1]))
    return send_reply(sptr, ERR_NORECIPIENT, MSG_NOTICE);

  if (parc < 3 || EmptyString(parv[parc - 1]))
    return send_reply(sptr, ERR_NOTEXTTOSEND);

  if (parv[1][0] == '@' && IsChannelPrefix(parv[1][1])) {
    parv[1]++;                        /* Get rid of '@' */
    return m_wallchops(cptr, sptr, parc, parv);
  }

  count = unique_name_vector(parv[1], ',', vector, MAXTARGETS);

  for (i = 0; i < count; ++i) {
    name = vector[i];
    /*
     * channel msg?
     */
    if (IsChannelPrefix(*name))
      relay_channel_notice(sptr, name, parv[parc - 1], count);

    else if (*name == '$')
      relay_masked_notice(sptr, name, parv[parc - 1]);

    else if ((server = strchr(name, '@')))
      relay_directed_notice(sptr, name, server, parv[parc - 1]);

    else 
      relay_private_notice(sptr, name, parv[parc - 1]);
  }
  return 0;
}
Beispiel #16
0
/*
 * do_clearmode(struct Client *cptr, struct Client *sptr,
 *		struct Channel *chptr, char *control)
 *
 * This is the function that actually clears the channel modes.
 */
static int
do_clearmode(struct Client *cptr, struct Client *sptr, struct Channel *chptr,
	     char *control)
{
  static int flags[] = {
    MODE_CHANOP,	'o',
    MODE_VOICE,		'v',
    MODE_PRIVATE,	'p',
    MODE_SECRET,	's',
    MODE_MODERATED,	'm',
    MODE_TOPICLIMIT,	't',
    MODE_INVITEONLY,	'i',
    MODE_NOPRIVMSGS,	'n',
    MODE_KEY,		'k',
    MODE_BAN,		'b',
    MODE_LIMIT,		'l',
    MODE_REGONLY,	'r',
    MODE_DELJOINS,      'D',
    0x0, 0x0
  };
  int *flag_p;
  unsigned int del_mode = 0;
  char control_buf[20];
  int control_buf_i = 0;
  struct ModeBuf mbuf;
  struct Ban *link, *next;
  struct Membership *member;

  /* Ok, so what are we supposed to get rid of? */
  for (; *control; control++) {
    for (flag_p = flags; flag_p[0]; flag_p += 2)
      if (*control == flag_p[1]) {
	del_mode |= flag_p[0];
	break;
      }
  }

  if (!del_mode)
    return 0; /* nothing to remove; ho hum. */

  modebuf_init(&mbuf, sptr, cptr, chptr,
	       (MODEBUF_DEST_CHANNEL | /* Send MODE to channel */
		MODEBUF_DEST_OPMODE  | /* Treat it like an OPMODE */
		MODEBUF_DEST_HACK4));  /* Generate a HACK(4) notice */

  modebuf_mode(&mbuf, MODE_DEL | (del_mode & chptr->mode.mode));
  chptr->mode.mode &= ~del_mode; /* and of course actually delete them */

  /* If we're removing invite, remove all the invites */
  if (del_mode & MODE_INVITEONLY)
    mode_invite_clear(chptr);

  /*
   * If we're removing the key, note that; note that we can't clear
   * the key until after modebuf_* are done with it
   */
  if (del_mode & MODE_KEY && *chptr->mode.key)
    modebuf_mode_string(&mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);

  /* If we're removing the limit, note that and clear the limit */
  if (del_mode & MODE_LIMIT && chptr->mode.limit) {
    modebuf_mode_uint(&mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit);
    chptr->mode.limit = 0; /* not referenced, so safe */
  }

  /*
   * Go through and mark the bans for deletion; note that we can't
   * free them until after modebuf_* are done with them
   */
  if (del_mode & MODE_BAN) {
    for (link = chptr->banlist; link; link = next) {
      char *bandup;
      next = link->next;

      DupString(bandup, link->banstr);
      modebuf_mode_string(&mbuf, MODE_DEL | MODE_BAN, /* delete ban */
			  bandup, 1);
      free_ban(link);
    }

    chptr->banlist = 0;
  }

  /* Deal with users on the channel */
  if (del_mode & (MODE_BAN | MODE_CHANOP | MODE_VOICE))
    for (member = chptr->members; member; member = member->next_member) {
      if (IsZombie(member)) /* we ignore zombies */
	continue;

      if (del_mode & MODE_BAN) /* If we cleared bans, clear the valid flags */
	ClearBanValid(member);

      /* Drop channel operator status */
      if (IsChanOp(member) && del_mode & MODE_CHANOP) {
	modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, member->user, MAXOPLEVEL + 1);
	member->status &= ~CHFL_CHANOP;
      }

      /* Drop voice */
      if (HasVoice(member) && del_mode & MODE_VOICE) {
	modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, member->user, MAXOPLEVEL + 1);
	member->status &= ~CHFL_VOICE;
      }
    }

  /* And flush the modes to the channel */
  modebuf_flush(&mbuf);

  /* Finally, we can clear the key... */
  if (del_mode & MODE_KEY)
    chptr->mode.key[0] = '\0';

  /* Ok, build control string again */
  for (flag_p = flags; flag_p[0]; flag_p += 2)
    if (del_mode & flag_p[0])
      control_buf[control_buf_i++] = flag_p[1];

  control_buf[control_buf_i] = '\0';

  /* Log it... */
  log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE, "%#C CLEARMODE %H %s", sptr,
	    chptr, control_buf);

  /* Then send it */
  if (!IsLocalChannel(chptr->chname))
    sendcmdto_serv_butone(sptr, CMD_CLEARMODE, cptr, "%H %s", chptr,
			  control_buf);

  return 0;
}
Beispiel #17
0
/*
 * ms_gline - server message handler
 *
 * parv[0] = Sender prefix
 * parv[1] = Target: server numeric
 * parv[2] = (+|-)<G-line mask>
 * parv[3] = G-line lifetime
 *
 * From Uworld:
 *
 * parv[4] = Comment
 *
 * From somewhere else:
 *
 * parv[4] = Last modification time
 * parv[5] = Comment
 *
 */
int
ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Client *acptr = 0;
  struct Gline *agline;
  unsigned int flags = 0;
  time_t expire_off, lastmod = 0;
  char *mask = parv[2], *target = parv[1], *reason = "No reason";

  if (*mask == '!')
  {
    mask++;
    flags |= GLINE_OPERFORCE; /* assume oper had WIDE_GLINE */
  }

  if ((parc == 3 && *mask == '-') || parc == 5)
  {
    if (!find_conf_byhost(cli_confs(cptr), cli_name(sptr), CONF_UWORLD))
      return need_more_params(sptr, "GLINE");

    flags |= GLINE_FORCE;
  }
  else if (parc > 5)
    lastmod = atoi(parv[4]);
  else
    return need_more_params(sptr, "GLINE");

  if (parc > 4)
    reason = parv[parc - 1];

  if (IsServer(sptr))
    flags |= GLINE_FORCE;

  if (!(target[0] == '*' && target[1] == '\0')) {
    if (!( (acptr = FindNServer(target)) || 
           (acptr = SeekServer(target)) ) )
      return 0; /* no such server */

    if (!IsMe(acptr)) { /* manually propagate */
      if (!lastmod)
	sendcmdto_one(sptr, CMD_GLINE, acptr,
		      (parc == 3) ? "%s %s" : "%s %s %s :%s", target, mask,
		      parv[3], reason);
      else
	sendcmdto_one(sptr, CMD_GLINE, acptr, "%s %s%s %s %s :%s", target,
		      flags & GLINE_OPERFORCE ? "!" : "", mask, parv[3],
		      parv[4], reason);

      return 0;
    }

    /* For asuka we don't want glines sent this way treated as local
     * flags |= GLINE_LOCAL;
     */
  }

  if (*mask == '-')
    mask++;
  else if (*mask == '+') {
    flags |= GLINE_ACTIVE;
    mask++;
  } else
    flags |= GLINE_ACTIVE;

  expire_off = parc < 5 ? 0 : atoi(parv[3]);

  agline = gline_find(mask, GLINE_ANY | GLINE_EXACT);

  if (agline) {
    if (GlineIsLocal(agline) && !(flags & GLINE_LOCAL)) /* global over local */
      gline_free(agline);
    else if (!lastmod && ((flags & GLINE_ACTIVE) == GlineIsRemActive(agline)))
      return gline_propagate(cptr, sptr, agline);
    else if (!lastmod || GlineLastMod(agline) < lastmod) { /* new mod */
      if (flags & GLINE_ACTIVE)
	return gline_activate(cptr, sptr, agline, lastmod, flags);
      else
	return gline_deactivate(cptr, sptr, agline, lastmod, flags);
    } else if (GlineLastMod(agline) == lastmod || IsBurstOrBurstAck(cptr))
      return 0;
    else
      return gline_resend(cptr, agline); /* other server desynched WRT gline */
  } else if (parc == 3 && !(flags & GLINE_ACTIVE)) {
    /* U-lined server removing a G-line we don't have; propagate the removal
     * anyway.
     */
    if (!(flags & GLINE_LOCAL))
      sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* -%s", mask);
    return 0;
  } else if (parc < 5)
    return need_more_params(sptr, "GLINE");

  return gline_add(cptr, sptr, mask, reason, expire_off, lastmod, flags);
}
Beispiel #18
0
/*
 * ms_mark - server message handler
 */
int ms_mark(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client* acptr;

  if (!IsServer(sptr))
    return protocol_violation(sptr, "MARK from non-server %s", cli_name(sptr));

  if (!strcmp(parv[2], MARK_WEBIRC)) {
    if(parc < 4)
      return protocol_violation(sptr, "MARK webirc received too few parameters (%u)", parc);

    if ((acptr = FindUser(parv[1]))) {
      ircd_strncpy(cli_webirc(acptr), parv[3], BUFSIZE);
      sendcmdto_serv_butone(sptr, CMD_MARK, cptr, "%s %s :%s", cli_name(acptr), MARK_WEBIRC, parv[3]);
    }
  } else if (!strcmp(parv[2], MARK_GEOIP)) {
    if(parc < 5)
      return protocol_violation(sptr, "MARK geoip received too few parameters (%u)", parc);
    if ((acptr = FindUser(parv[1]))) {
      geoip_apply_mark(acptr, parv[3], parv[4], (parc > 5 ? parv[5] : NULL));
      if (parc > 5)
        sendcmdto_serv_butone(sptr, CMD_MARK, cptr, "%s %s %s %s :%s", cli_name(acptr), MARK_GEOIP, parv[3], parv[4], parv[5]);
      else
        sendcmdto_serv_butone(sptr, CMD_MARK, cptr, "%s %s %s %s", cli_name(acptr), MARK_GEOIP, parv[3], parv[4]);
    }
  } else if (!strcmp(parv[2], MARK_CVERSION)) {
    if(parc < 4)
      return protocol_violation(sptr, "MARK client version received too few parameters (%u)", parc);

    if ((acptr = FindUser(parv[1]))) {
       ircd_strncpy(cli_version(acptr), parv[3], VERSIONLEN);
       sendcmdto_serv_butone(sptr, CMD_MARK, cptr, "%s %s :%s", cli_name(acptr), MARK_CVERSION, parv[3]);
    }
  } else if (!strcmp(parv[2], MARK_SSLCLIFP)) {
    if(parc < 4)
      return protocol_violation(sptr, "MARK SSL client certificate fingerprint received too few parameters (%u)", parc);

    if ((acptr = FindUser(parv[1]))) {
       ircd_strncpy(cli_sslclifp(acptr), parv[3], BUFSIZE);
       sendcmdto_serv_butone(sptr, CMD_MARK, cptr, "%s %s :%s", cli_name(acptr), MARK_SSLCLIFP, parv[3]);
    }
  } else if (!strcmp(parv[2], MARK_KILL)) {
    if(parc < 4)
      return protocol_violation(sptr, "MARK kill received too few parameters (%u)", parc);

    if ((acptr = FindUser(parv[1]))) {
      ircd_strncpy(cli_killmark(acptr), parv[3], BUFSIZE);
      sendcmdto_serv_butone(sptr, CMD_MARK, cptr, "%s %s :%s", cli_name(acptr), MARK_KILL, parv[3]);
    }
  } else if (!strcmp(parv[2], MARK_MARK) || !strcmp(parv[2], MARK_DNSBL_DATA)) {
    if(parc < 4)
      return protocol_violation(sptr, "MARK MARK (tag) received too few parameters (%u)", parc);

    if ((acptr = FindUser(parv[1]))) {
      add_mark(acptr, parv[3]);
      SetMarked(acptr);
      sendcmdto_serv_butone(sptr, CMD_MARK, cptr, "%s %s :%s", cli_name(acptr), MARK_MARK, parv[3]);
    }
  }

  return 0;
}
Beispiel #19
0
/** Apply and send silence updates for a user.
 * @param[in] sptr Client whose silence list has been updated.
 * @param[in] silences Comma-separated list of silence updates.
 * @param[in] dest Direction to send updates in (NULL for broadcast).
 */
static void
forward_silences(struct Client *sptr, char *silences, struct Client *dest)
{
  struct Ban *accepted[MAXPARA], *sile, **plast;
  char *cp, *p, buf[BUFSIZE];
  size_t ac_count, buf_used, slen, ii;

  /* Split the list of silences and try to apply each one in turn. */
  for (cp = ircd_strtok(&p, silences, ","), ac_count = 0;
       cp && (ac_count < MAXPARA);
       cp = ircd_strtok(&p, 0, ",")) {
    if ((sile = apply_silence(sptr, cp)))
      accepted[ac_count++] = sile;
  }

  if (MyUser(sptr)) {
    size_t siles, maxsiles, totlength, maxlength, jj;

    /* Check that silence count and total length are permitted. */
    maxsiles = feature_int(FEAT_MAXSILES);
    maxlength = maxsiles * feature_int(FEAT_AVBANLEN);
    siles = totlength = 0;
    /* Count number of current silences and their total length. */
    plast = &cli_user(sptr)->silence;
    for (sile = cli_user(sptr)->silence; sile; sile = sile->next) {
      if (sile->flags & (BAN_OVERLAPPED | BAN_ADD | BAN_DEL))
        continue;
      siles++;
      totlength += strlen(sile->banstr);
      plast = &sile->next;
    }
    for (ii = jj = 0; ii < ac_count; ++ii) {
      sile = accepted[ii];
      /* If the update is being added, and we would exceed the maximum
       * count or length, drop the update.
       */
      if (!(sile->flags & (BAN_OVERLAPPED | BAN_DEL))) {
        slen = strlen(sile->banstr);
        if ((siles >= maxsiles) || (totlength + slen >= maxlength)) {
          *plast = NULL;
          if (MyUser(sptr))
            send_reply(sptr, ERR_SILELISTFULL, accepted[ii]->banstr);
          free_ban(accepted[ii]);
          continue;
        }
        /* Update counts. */
        siles++;
        totlength += slen;
        plast = &sile->next;
      }
      /* Store the update. */
      accepted[jj++] = sile;
    }
    /* Write back the number of accepted updates. */
    ac_count = jj;

    /* Send the silence update list, including overlapped silences (to
     * make it easier on clients).
     */
    buf_used = 0;
    for (sile = cli_user(sptr)->silence; sile; sile = sile->next) {
      char ch;
      if (sile->flags & (BAN_OVERLAPPED | BAN_DEL))
        ch = '-';
      else if (sile->flags & BAN_ADD)
        ch = '+';
      else
        continue;
      slen = strlen(sile->banstr);
      if (buf_used + slen + 4 > 400) {
        buf[buf_used] = '\0';
        sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf);
        buf_used = 0;
      }
      if (buf_used)
        buf[buf_used++] = ',';
      buf[buf_used++] = ch;
      if (sile->flags & BAN_EXCEPTION)
        buf[buf_used++] = '~';
      memcpy(buf + buf_used, sile->banstr, slen);
      buf_used += slen;
    }
    if (buf_used > 0) {
        buf[buf_used] = '\0';
        sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf);
        buf_used = 0;
    }
  }

  /* Forward any silence removals or exceptions updates to other
   * servers if the user has positive silences.
   */
  if (!dest || !MyUser(dest)) {
    for (ii = buf_used = 0; ii < ac_count; ++ii) {
      char ch;
      sile = accepted[ii];
      if (sile->flags & BAN_OVERLAPPED)
        continue;
      else if (sile->flags & BAN_DEL)
        ch = '-';
      else if (sile->flags & BAN_ADD) {
        if (!(sile->flags & BAN_EXCEPTION))
          continue;
        ch = '+';
      } else
        continue;
      slen = strlen(sile->banstr);
      if (buf_used + slen + 4 > 400) {
        buf[buf_used] = '\0';
        if (dest)
          sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf);
        else
          sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf);
        buf_used = 0;
      }
      if (buf_used)
        buf[buf_used++] = ',';
      buf[buf_used++] = ch;
      if (sile->flags & BAN_EXCEPTION)
        buf[buf_used++] = '~';
      memcpy(buf + buf_used, sile->banstr, slen);
      buf_used += slen;
    }
    if (buf_used > 0) {
        buf[buf_used] = '\0';
        if (dest)
          sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf);
        else
          sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf);
        buf_used = 0;
    }
  }

  /* Remove overlapped and deleted silences from the user's silence
   * list.  Clear BAN_ADD since we're walking the list anyway.
   */
  for (plast = &cli_user(sptr)->silence; (sile = *plast) != NULL; ) {
    if (sile->flags & (BAN_OVERLAPPED | BAN_DEL)) {
      *plast = sile->next;
      free_ban(sile);
    } else {
      sile->flags &= ~BAN_ADD;
      *plast = sile;
      plast = &sile->next;
    }
  }

  /* Free any silence-deleting updates. */
  for (ii = 0; ii < ac_count; ++ii) {
    if ((accepted[ii]->flags & (BAN_ADD | BAN_DEL)) == BAN_DEL) {
      free_ban(accepted[ii]);
    }
  }
}
Beispiel #20
0
/*
 * mo_settime - oper message handler
 *
 * parv[0] = sender prefix
 * parv[1] = new time
 * parv[2] = servername (Only used when sptr is an Oper).
 */
int mo_settime(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  time_t t;
  long dt;
  static char tbuf[11];

  /* Must be a global oper */
  if (!IsOper(sptr))
    return send_reply(sptr, ERR_NOPRIVILEGES);

  if (parc < 2) /* verify argument count */
    return need_more_params(sptr, "SETTIME");

  if (parc == 2 && MyUser(sptr)) /* default to me */
    parv[parc++] = cli_name(&me);

  t = atoi(parv[1]); /* convert the time */

  /* If we're reliable_clock or if the oper specified a 0 time, use current */
  if (!t || feature_bool(FEAT_RELIABLE_CLOCK))
  {
    t = TStime();
    ircd_snprintf(0, tbuf, sizeof(tbuf), "%Tu", TStime());
    parv[1] = tbuf;
  }

  dt = TStime() - t; /* calculate the delta */

  if (t < OLDEST_TS || dt < -9000000) /* verify value */
  {
    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :SETTIME: Bad value", sptr);
    return 0;
  }

  /* OK, send the message off to its destination */
  if (hunt_server_prio_cmd(sptr, CMD_SETTIME, cptr, 1, "%s %C", 2, parc,
                           parv) != HUNTED_ISME)
    return 0;

  if (feature_bool(FEAT_RELIABLE_CLOCK)) /* don't apply settime--reliable */
  {
    if ((dt > 600) || (dt < -600))
      sendcmdto_serv_butone(&me, CMD_DESYNCH, 0, ":Bad SETTIME from %s: %Tu "
                            "(delta %ld)", cli_name(sptr), t, dt);
    if (IsUser(sptr)) /* Let user know we're ignoring him */
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is not set %ld seconds "
                    "%s: RELIABLE_CLOCK is defined", sptr, (dt < 0) ? -dt : dt,
                    (dt < 0) ? "forwards" : "backwards");
  }
  else /* tell opers about time change */
  {
    sendto_opmask_butone(0, SNO_OLDSNO, "SETTIME from %s, clock is set %ld "
			 "seconds %s", cli_name(sptr), (dt < 0) ? -dt : dt,
			 (dt < 0) ? "forwards" : "backwards");
    TSoffset -= dt; /* apply time change */
    if (IsUser(sptr)) /* let user know what we did */
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is set %ld seconds %s",
		    sptr, (dt < 0) ? -dt : dt,
		    (dt < 0) ? "forwards" : "backwards");
  }

  return 0;
}
Beispiel #21
0
/*
 * ms_settime - server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = new time
 * parv[2] = server name (Only used when sptr is an Oper).
 */
int ms_settime(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  time_t t;
  long dt;
  static char tbuf[11];
  struct DLink *lp;

  if (parc < 2) /* verify argument count */
    return need_more_params(sptr, "SETTIME");

  t = atoi(parv[1]); /* convert time and compute delta */
  dt = TStime() - t;

  /* verify value */
  if (t < OLDEST_TS || dt < -9000000)
  {
    if (IsServer(sptr)) /* protocol violation if it's from a server */
      protocol_violation(sptr, "SETTIME: Bad value (%Tu, delta %ld)", t, dt);
    else
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :SETTIME: Bad value (%Tu, "
                    "delta %ld)", sptr, t, dt);
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :SETTIME: Bad value", sptr);
    return 0;
  }

  /* reset time... */
  if (feature_bool(FEAT_RELIABLE_CLOCK))
  {
    ircd_snprintf(0, tbuf, sizeof(tbuf), "%Tu", TStime());
    parv[1] = tbuf;
  }

  if (BadPtr(parv[2])) /* spam the network */
  {
    for (lp = cli_serv(&me)->down; lp; lp = lp->next)
      if (cptr != lp->value.cptr)
        sendcmdto_prio_one(sptr, CMD_SETTIME, lp->value.cptr, "%s", parv[1]);
  }
  else
  {
    if (hunt_server_prio_cmd(sptr, CMD_SETTIME, cptr, 1, "%s %C", 2, parc,
                             parv) != HUNTED_ISME)
    {
      /* If the destination was *not* me, but I'm RELIABLE_CLOCK and the
       * delta is more than 30 seconds off, bounce back a corrected
       * SETTIME
       */
      if (feature_bool(FEAT_RELIABLE_CLOCK) && (dt > 30 || dt < -30))
        sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%s %C", parv[1], cptr);
      return 0;
    }
  }

  if (feature_bool(FEAT_RELIABLE_CLOCK))
  {
    /* don't apply settime--reliable */
    if ((dt > 600) || (dt < -600))
      sendcmdto_serv_butone(&me, CMD_DESYNCH, 0, ":Bad SETTIME from %s: %Tu "
                            "(delta %ld)", cli_name(sptr), t, dt);
    /* Let user know we're ignoring him */
    if (IsUser(sptr))
    {
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is not set %ld "
		    "seconds %s : RELIABLE_CLOCK is defined", sptr,
		    (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
    }
  }
  else /* tell opers about time change */
  {
    sendto_opmask_butone(0, SNO_OLDSNO, "SETTIME from %s, clock is set %ld "
			 "seconds %s", cli_name(sptr), (dt < 0) ? -dt : dt,
			 (dt < 0) ? "forwards" : "backwards");
    /* Apply time change... */
    TSoffset -= dt;
    /* Let the issuing user know what we did... */
    if (IsUser(sptr))
    {
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :clock is set %ld seconds %s",
                    sptr, (dt < 0) ? -dt : dt,
                    (dt < 0) ? "forwards" : "backwards");
    }
  }

  return 0;
}
Beispiel #22
0
/** Modify a global Z-line.
 * @param[in] cptr Client that sent us the Z-line modification.
 * @param[in] sptr Client that originated the Z-line modification.
 * @param[in] zline Z-line being modified.
 * @param[in] action Resultant status of the Z-line.
 * @param[in] reason Reason for Z-line.
 * @param[in] expire Expiration time of Z-line.
 * @param[in] lastmod Last modification time of Z-line.
 * @param[in] lifetime Lifetime of Z-line.
 * @param[in] flags Bitwise combination of ZLINE_* flags.
 * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal.
 */
int
zline_modify(struct Client *cptr, struct Client *sptr, struct Zline *zline,
	     enum ZlineAction action, char *reason, time_t expire,
	     time_t lastmod, time_t lifetime, unsigned int flags)
{
  char buf[BUFSIZE], *op = "";
  int pos = 0, non_auto = 0;

  assert(zline);
  assert(!ZlineIsLocal(zline));

  Debug((DEBUG_DEBUG,  "zline_modify(\"%s\", \"%s\", \"%s\", %s, \"%s\", "
	 "%Tu, %Tu, %Tu, 0x%04x)", cli_name(cptr), cli_name(sptr),
	 zline->zl_mask,
	 action == ZLINE_ACTIVATE ? "ZLINE_ACTIVATE" :
	 (action == ZLINE_DEACTIVATE ? "ZLINE_DEACTIVATE" :
	  (action == ZLINE_LOCAL_ACTIVATE ? "ZLINE_LOCAL_ACTIVATE" :
	   (action == ZLINE_LOCAL_DEACTIVATE ? "ZLINE_LOCAL_DEACTIVATE" :
	    (action == ZLINE_MODIFY ? "ZLINE_MODIFY" : "<UNKNOWN>")))),
	 reason, expire, lastmod, lifetime, flags));

  /* First, let's check lastmod... */
  if (action != ZLINE_LOCAL_ACTIVATE && action != ZLINE_LOCAL_DEACTIVATE) {
    if (ZlineLastMod(zline) > lastmod) { /* we have a more recent version */
      if (IsBurstOrBurstAck(cptr))
	return 0; /* middle of a burst, it'll resync on its own */
      return zline_resend(cptr, zline); /* resync the server */
    } else if (ZlineLastMod(zline) == lastmod)
      return 0; /* we have that version of the Z-line... */
  }

  /* All right, we know that there's a change of some sort.  What is it? */
  /* first, check out the expiration time... */
  if ((flags & ZLINE_EXPIRE) && expire) {
    if (!(flags & ZLINE_FORCE) &&
	(expire <= TStime() || expire > TStime() + ZLINE_MAX_EXPIRE)) {
      if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */
	send_reply(sptr, ERR_BADEXPIRE, expire);
      return 0;
    }
  } else
    flags &= ~ZLINE_EXPIRE;

  /* Now check to see if there's any change... */
  if ((flags & ZLINE_EXPIRE) && expire == zline->zl_expire) {
    flags &= ~ZLINE_EXPIRE; /* no change to expiration time... */
    expire = 0;
  }

  /* Next, check out lifetime--this one's a bit trickier... */
  if (!(flags & ZLINE_LIFETIME) || !lifetime)
    lifetime = zline->zl_lifetime; /* use Z-line lifetime */

  lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */

  /* OK, let's see which is greater... */
  if (lifetime > zline->zl_lifetime)
    flags |= ZLINE_LIFETIME; /* have to update lifetime */
  else {
    flags &= ~ZLINE_LIFETIME; /* no change to lifetime */
    lifetime = 0;
  }

  /* Finally, let's see if the reason needs to be updated */
  if ((flags & ZLINE_REASON) && reason &&
      !ircd_strcmp(zline->zl_reason, reason))
    flags &= ~ZLINE_REASON; /* no changes to the reason */

  /* OK, now let's take a look at the action... */
  if ((action == ZLINE_ACTIVATE && (zline->zl_flags & ZLINE_ACTIVE)) ||
      (action == ZLINE_DEACTIVATE && !(zline->zl_flags & ZLINE_ACTIVE)) ||
      (action == ZLINE_LOCAL_ACTIVATE &&
       (zline->zl_state == ZLOCAL_ACTIVATED)) ||
      (action == ZLINE_LOCAL_DEACTIVATE &&
       (zline->zl_state == ZLOCAL_DEACTIVATED)) ||
      /* can't activate an expired Z-line */
      IRCD_MAX(zline->zl_expire, expire) <= TStime())
    action = ZLINE_MODIFY; /* no activity state modifications */

  Debug((DEBUG_DEBUG,  "About to perform changes; flags 0x%04x, action %s",
	 flags, action == ZLINE_ACTIVATE ? "ZLINE_ACTIVATE" :
	 (action == ZLINE_DEACTIVATE ? "ZLINE_DEACTIVATE" :
	  (action == ZLINE_LOCAL_ACTIVATE ? "ZLINE_LOCAL_ACTIVATE" :
	   (action == ZLINE_LOCAL_DEACTIVATE ? "ZLINE_LOCAL_DEACTIVATE" :
	    (action == ZLINE_MODIFY ? "ZLINE_MODIFY" : "<UNKNOWN>"))))));

  /* If there are no changes to perform, do no changes */
  if (!(flags & ZLINE_UPDATE) && action == ZLINE_MODIFY)
    return 0;

  /* Now we know what needs to be changed, so let's process the changes... */

  /* Start by updating lastmod, if indicated... */
  if (action != ZLINE_LOCAL_ACTIVATE && action != ZLINE_LOCAL_DEACTIVATE)
    zline->zl_lastmod = lastmod;

  /* Then move on to activity status changes... */
  switch (action) {
  case ZLINE_ACTIVATE: /* Globally activating Z-line */
    zline->zl_flags |= ZLINE_ACTIVE; /* make it active... */
    zline->zl_state = ZLOCAL_GLOBAL; /* reset local activity state */
    pos += ircd_snprintf(0, buf, sizeof(buf), " globally activating Z-line");
    op = "+"; /* operation for Z-line propagation */
    break;

  case ZLINE_DEACTIVATE: /* Globally deactivating Z-line */
    zline->zl_flags &= ~ZLINE_ACTIVE; /* make it inactive... */
    zline->zl_state = ZLOCAL_GLOBAL; /* reset local activity state */
    pos += ircd_snprintf(0, buf, sizeof(buf), " globally deactivating Z-line");
    op = "-"; /* operation for Z-line propagation */
    break;

  case ZLINE_LOCAL_ACTIVATE: /* Locally activating Z-line */
    zline->zl_state = ZLOCAL_ACTIVATED; /* make it locally active */
    pos += ircd_snprintf(0, buf, sizeof(buf), " locally activating Z-line");
    break;

  case ZLINE_LOCAL_DEACTIVATE: /* Locally deactivating Z-line */
    zline->zl_state = ZLOCAL_DEACTIVATED; /* make it locally inactive */
    pos += ircd_snprintf(0, buf, sizeof(buf), " locally deactivating Z-line");
    break;

  case ZLINE_MODIFY: /* no change to activity status */
    break;
  }

  /* Handle expiration changes... */
  if (flags & ZLINE_EXPIRE) {
    zline->zl_expire = expire; /* save new expiration time */
    if (pos < BUFSIZE)
      pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
			   "%s%s changing expiration time to %Tu",
			   pos ? ";" : "",
			   pos && !(flags & (ZLINE_LIFETIME | ZLINE_REASON)) ?
			   " and" : "", expire);
  }

  /* Next, handle lifetime changes... */
  if (flags & ZLINE_LIFETIME) {
    zline->zl_lifetime = lifetime; /* save new lifetime */
    if (pos < BUFSIZE)
      pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
			   "%s%s extending record lifetime to %Tu",
			   pos ? ";" : "", pos && !(flags & ZLINE_REASON) ?
			   " and" : "", lifetime);
  }

  /* Now, handle reason changes... */
  if (flags & ZLINE_REASON) {
    non_auto = non_auto || ircd_strncmp(zline->zl_reason, "AUTO", 4);
    MyFree(zline->zl_reason); /* release old reason */
    DupString(zline->zl_reason, reason); /* store new reason */
    if (pos < BUFSIZE)
      pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos,
			   "%s%s changing reason to \"%s\"",
			   pos ? ";" : "", pos ? " and" : "", reason);
  }

  /* All right, inform ops... */
  non_auto = non_auto || ircd_strncmp(zline->zl_reason, "AUTO", 4);
  sendto_opmask_butone(0, non_auto ? SNO_GLINE : SNO_AUTO,
		       "%s modifying global ZLINE for %s:%s",
		       (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
		       cli_name(sptr) : cli_name((cli_user(sptr))->server),
		       zline->zl_mask, buf);

  /* and log the change */
  log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
	    "%#C modifying global ZLINE for %s:%s", sptr, zline->zl_mask, buf);

  /* We'll be simple for this release, but we can update this to change
   * the propagation syntax on future updates
   */
  if (action != ZLINE_LOCAL_ACTIVATE && action != ZLINE_LOCAL_DEACTIVATE)
    sendcmdto_serv_butone(sptr, CMD_ZLINE, cptr,
			  "* %s%s%s %Tu %Tu %Tu :%s",
			  flags & ZLINE_OPERFORCE ? "!" : "", op,
			  zline->zl_mask, zline->zl_expire - TStime(),
                          zline->zl_lastmod, zline->zl_lifetime, zline->zl_reason);

  /* OK, let's do the Z-line... */
  return do_zline(cptr, sptr, zline);
}
/*
 * ms_nick - server message handler for nicks
 * parv[0] = sender prefix
 * parv[1] = nickname
 *
 * If from server, source is client:
 *   parv[2] = timestamp
 *
 * Source is server:
 *   parv[2] = hopcount
 *   parv[3] = timestamp
 *   parv[4] = username
 *   parv[5] = hostname
 *   parv[6] = umode (optional)
 *   parv[parc-3] = IP#                 <- Only Protocol >= 10
 *   parv[parc-2] = YXX, numeric nick   <- Only Protocol >= 10
 *   parv[parc-1] = info
 *   parv[0] = server
 */
int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client *acptr;
  char nick[NICKLEN + 2];
  time_t lastnick = 0;
  int differ = 1;
  const char *type;

  assert(0 != cptr);
  assert(0 != sptr);
  assert(IsServer(cptr));

  if ((IsServer(sptr) && parc < 8) || parc < 3)
  {
    sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C",
			 parv[1], cptr);
    return need_more_params(sptr, "NICK");
  }

  ircd_strncpy(nick, parv[1], NICKLEN);
  nick[NICKLEN] = '\0';

  if (IsServer(sptr))
  {
    lastnick = atoi(parv[3]);
    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr)) 
      cli_serv(sptr)->lag = TStime() - lastnick;
  }
  else
  {
    lastnick = atoi(parv[2]); 
    if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr))
      cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick;
  }
  /*
   * If do_nick_name() returns a null name OR if the server sent a nick
   * name and do_nick_name() changed it in some way (due to rules of nick
   * creation) then reject it. If from a server and we reject it,
   * and KILL it. -avalon 4/4/92
   */
  if (!do_nick_name(nick) || strcmp(nick, parv[1]))
  {
    send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]);
    
    ++ServerStats->is_kill;
    sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1],
			 parv[0], cptr);
    sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])",
		  IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1],
		  nick, cli_name(cptr));
    if (!IsServer(sptr))
    {
      /*
       * bad nick _change_
       */
      sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)",
			    parv[0], cli_name(&me), cli_name(cptr), parv[0],
			    cli_user(sptr) ? cli_username(sptr) : "",
			    cli_user(sptr) ? cli_name(cli_user(sptr)->server) :
			    cli_name(cptr));
    }
    return 0;
  }
  /* Check against nick name collisions. */
  if ((acptr = FindClient(nick)) == NULL)
    /* No collisions, all clear... */
    return set_nick_name(cptr, sptr, nick, parc, parv);

  /*
   * If acptr == sptr, then we have a client doing a nick
   * change between *equivalent* nicknames as far as server
   * is concerned (user is changing the case of his/her
   * nickname or somesuch)
   */
  if (acptr == sptr)
  {
    if (strcmp(cli_name(acptr), nick) != 0)
      /* Allows change of case in his/her nick */
      return set_nick_name(cptr, sptr, nick, parc, parv);
    else
      /* Setting their nick to what it already is? Ignore it. */
      return 0;
  }
  /* now we know we have a real collision. */
  /*
   * Note: From this point forward it can be assumed that
   * acptr != sptr (point to different client structures).
   */
  assert(acptr != sptr);
  /*
   * If the older one is "non-person", the new entry is just
   * allowed to overwrite it. Just silently drop non-person,
   * and proceed with the nick. This should take care of the
   * "dormant nick" way of generating collisions...
   */
  if (IsUnknown(acptr) && MyConnect(acptr))
  {
    ServerStats->is_ref++;
    IPcheck_connect_fail(acptr);
    exit_client(cptr, acptr, &me, "Overridden by other sign on");
    return set_nick_name(cptr, sptr, nick, parc, parv);
  }
  /*
   * Decide, we really have a nick collision and deal with it
   */
  /*
   * NICK was coming from a server connection.
   * This means we have a race condition (two users signing on
   * at the same time), or two net fragments reconnecting with the same nick.
   * The latter can happen because two different users connected
   * or because one and the same user switched server during a net break.
   * If the TimeStamps are equal, we kill both (or only 'new'
   * if it was a ":server NICK new ...").
   * Otherwise we kill the youngest when user@host differ,
   * or the oldest when they are the same.
   * We treat user and ~user as different, because if it wasn't
   * a faked ~user the AUTH wouldn't have added the '~'.
   * --Run
   *
   */
  if (IsServer(sptr))
  {
    struct irc_in_addr ip;
    /*
     * A new NICK being introduced by a neighbouring
     * server (e.g. message type ":server NICK new ..." received)
     *
     * compare IP address and username
     */
    base64toip(parv[parc - 3], &ip);
    differ =  (0 != memcmp(&cli_ip(acptr), &ip, sizeof(cli_ip(acptr)))) ||
              (0 != ircd_strcmp(cli_user(acptr)->username, parv[4]));
    sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- "
			 "%C %Tu (%s user@host))", acptr, cli_from(acptr),
			 cli_lastnick(acptr), cptr, lastnick,
			 differ ? "Different" : "Same");
  }
  else
  {
    /*
     * A NICK change has collided (e.g. message type ":old NICK new").
     *
     * compare IP address and username
     */
    differ =  (0 != memcmp(&cli_ip(acptr), &cli_ip(sptr), sizeof(cli_ip(acptr)))) ||
              (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username));
    sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to "
			 "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr),
			 cli_lastnick(acptr), cptr, lastnick);
  }
  type = differ ? "overruled by older nick" : "nick collision from same user@host";
  /*
   * Now remove (kill) the nick on our side if it is the youngest.
   * If no timestamp was received, we ignore the incoming nick
   * (and expect a KILL for our legit nick soon ):
   * When the timestamps are equal we kill both nicks. --Run
   * acptr->from != cptr should *always* be true (?).
   *
   * This exits the client sending the NICK message
   */
  if ((differ && lastnick >= cli_lastnick(acptr)) ||
      (!differ && lastnick <= cli_lastnick(acptr)))
  {
    ServerStats->is_kill++;
    if (!IsServer(sptr))
    {
      /* If this was a nick change and not a nick introduction, we
       * need to ensure that we remove our record of the client, and
       * send a KILL to the whole network.
       */
      assert(!MyConnect(sptr));
      /* Inform the rest of the net... */
      sendcmdto_serv_butone(&me, CMD_KILL, 0, "%C :%s (%s)",
                            sptr, cli_name(&me), type);
      /* Don't go sending off a QUIT message... */
      SetFlag(sptr, FLAG_KILLED);
      /* Remove them locally. */
      exit_client_msg(cptr, sptr, &me,
                      "Killed (%s (%s))",
                      feature_str(FEAT_HIS_SERVERNAME), type);
    }
    else
    {
      /* If the origin is a server, this was a new client, so we only
       * send the KILL in the direction it came from.  We have no
       * client record that we would have to clean up.
       */
      sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s)",
                    parv[parc - 2], cli_name(&me), type);
    }
    /* If the timestamps differ and we just killed sptr, we don't need to kill
     * acptr as well.
     */
    if (lastnick != cli_lastnick(acptr))
      return 0;
  }
  /* Tell acptr why we are killing it. */
  send_reply(acptr, ERR_NICKCOLLISION, nick);

  ServerStats->is_kill++;
  SetFlag(acptr, FLAG_KILLED);
  /*
   * This exits the client we had before getting the NICK message
   */
  sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (%s)",
                        acptr, feature_str(FEAT_HIS_SERVERNAME),
                        type);
  exit_client_msg(cptr, acptr, &me, "Killed (%s (%s))",
                  feature_str(FEAT_HIS_SERVERNAME), type);
  if (lastnick == cli_lastnick(acptr))
    return 0;
  if (sptr == NULL)
    return 0;
  return set_nick_name(cptr, sptr, nick, parc, parv);
}
/** Check whether the introduction of a new server would cause a loop
 * or be disallowed by leaf and hub configuration directives.
 * @param[in] cptr Neighbor who sent the message.
 * @param[in] sptr Client that originated the message.
 * @param[out] ghost If non-NULL, receives ghost timestamp for new server.
 * @param[in] host Name of new server.
 * @param[in] numnick Numnick mask of new server.
 * @param[in] timestamp Claimed link timestamp of new server.
 * @param[in] hop Number of hops to the new server.
 * @param[in] junction Non-zero if the new server is still bursting.
 * @return CPTR_KILLED if \a cptr was SQUIT.  0 if some other server
 * was SQUIT.  1 if the new server is allowed.
 */
static int
check_loop_and_lh(struct Client* cptr, struct Client *sptr, time_t *ghost, const char *host, const char *numnick, time_t timestamp, int hop, int junction)
{
  struct Client* acptr;
  struct Client* LHcptr = NULL;
  struct ConfItem* lhconf;
  enum lh_type active_lh_line = ALLOWED;
  int ii;

  if (ghost)
    *ghost = 0;

  /*
   * Calculate type of connect limit and applicable config item.
   */
  lhconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER);
  assert(lhconf != NULL);
  if (ghost)
  {
    if (!feature_bool(FEAT_HUB))
      for (ii = 0; ii <= HighestFd; ii++)
        if (LocalClientArray[ii] && IsServer(LocalClientArray[ii])) {
          active_lh_line = I_AM_NOT_HUB;
          break;
        }
  }
  else if (hop > lhconf->maximum)
  {
    /* Because "maximum" should be 0 for non-hub links, check whether
     * there is a hub mask -- if not, complain that the server isn't
     * allowed to hub.
     */
    active_lh_line = lhconf->hub_limit ? MAX_HOPS_EXCEEDED : NOT_ALLOWED_TO_HUB;
  }
  else if (lhconf->hub_limit && match(lhconf->hub_limit, host))
  {
    struct Client *ac3ptr;
    active_lh_line = NOT_ALLOWED_TO_HUB;
    if (junction)
      for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
        if (IsJunction(ac3ptr)) {
          LHcptr = ac3ptr;
          break;
        }
  }

  /*
   *  We want to find IsConnecting() and IsHandshake() too,
   *  use FindClient().
   *  The second finds collisions with numeric representation of existing
   *  servers - these shouldn't happen anymore when all upgraded to 2.10.
   *  -- Run
   */
  while ((acptr = FindClient(host))
         || (numnick && (acptr = FindNServer(numnick))))
  {
    /*
     *  This link is trying feed me a server that I already have
     *  access through another path
     *
     *  Do not allow Uworld to do this.
     *  Do not allow servers that are juped.
     *  Do not allow servers that have older link timestamps
     *    then this try.
     *  Do not allow servers that use the same numeric as an existing
     *    server, but have a different name.
     *
     *  If my ircd.conf sucks, I can try to connect to myself:
     */
    if (acptr == &me)
      return exit_client_msg(cptr, cptr, &me, "nick collision with me (%s), check server number in M:?", host);
    /*
     * Detect wrong numeric.
     */
    if (0 != ircd_strcmp(cli_name(acptr), host))
    {
      sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
			    ":SERVER Numeric Collision: %s != %s",
			    cli_name(acptr), host);
      return exit_client_msg(cptr, cptr, &me,
          "NUMERIC collision between %s and %s."
          " Is your server numeric correct ?", host, cli_name(acptr));
    }
    /*
     *  Kill our try, if we had one.
     */
    if (IsConnecting(acptr))
    {
      if (active_lh_line == ALLOWED && exit_client(cptr, acptr, &me,
          "Just connected via another link") == CPTR_KILLED)
        return CPTR_KILLED;
      /*
       * We can have only ONE 'IsConnecting', 'IsHandshake' or
       * 'IsServer', because new 'IsConnecting's are refused to
       * the same server if we already had it.
       */
      break;
    }
    /*
     * Avoid other nick collisions...
     * This is a doubtful test though, what else would it be
     * when it has a server.name ?
     */
    else if (!IsServer(acptr) && !IsHandshake(acptr))
      return exit_client_msg(cptr, cptr, &me,
                             "Nickname %s already exists!", host);
    /*
     * Our new server might be a juped server,
     * or someone trying abuse a second Uworld:
     */
    else if (IsServer(acptr) && (0 == ircd_strncmp(cli_info(acptr), "JUPE", 4) ||
        find_conf_byhost(cli_confs(cptr), cli_name(acptr), CONF_UWORLD)))
    {
      if (!IsServer(sptr))
        return exit_client(cptr, sptr, &me, cli_info(acptr));
      sendcmdto_serv_butone(&me, CMD_WALLOPS, cptr,
			    ":Received :%s SERVER %s from %s !?!",
                            NumServ(cptr), host, cli_name(cptr));
      return exit_new_server(cptr, sptr, host, timestamp, "%s", cli_info(acptr));
    }
    /*
     * Of course we find the handshake this link was before :)
     */
    else if (IsHandshake(acptr) && acptr == cptr)
      break;
    /*
     * Here we have a server nick collision...
     * We don't want to kill the link that was last /connected,
     * but we neither want to kill a good (old) link.
     * Therefor we kill the second youngest link.
     */
    if (1)
    {
      struct Client* c2ptr = 0;
      struct Client* c3ptr = acptr;
      struct Client* ac2ptr;
      struct Client* ac3ptr;

      /* Search youngest link: */
      for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
        if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
          c3ptr = ac3ptr;
      if (IsServer(sptr))
      {
        for (ac3ptr = sptr; ac3ptr != &me; ac3ptr = cli_serv(ac3ptr)->up)
          if (cli_serv(ac3ptr)->timestamp > cli_serv(c3ptr)->timestamp)
            c3ptr = ac3ptr;
      }
      if (timestamp > cli_serv(c3ptr)->timestamp)
      {
        c3ptr = 0;
        c2ptr = acptr;          /* Make sure they differ */
      }
      /* Search second youngest link: */
      for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
        if (ac2ptr != c3ptr &&
            cli_serv(ac2ptr)->timestamp >
            (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
          c2ptr = ac2ptr;
      if (IsServer(sptr))
      {
        for (ac2ptr = sptr; ac2ptr != &me; ac2ptr = cli_serv(ac2ptr)->up)
          if (ac2ptr != c3ptr &&
              cli_serv(ac2ptr)->timestamp >
              (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
            c2ptr = ac2ptr;
      }
      if (c3ptr && timestamp > (c2ptr ? cli_serv(c2ptr)->timestamp : timestamp))
        c2ptr = 0;
      /* If timestamps are equal, decide which link to break
       *  by name.
       */
      if ((c2ptr ? cli_serv(c2ptr)->timestamp : timestamp) ==
          (c3ptr ? cli_serv(c3ptr)->timestamp : timestamp))
      {
        const char *n2, *n2up, *n3, *n3up;
        if (c2ptr)
        {
          n2 = cli_name(c2ptr);
          n2up = MyConnect(c2ptr) ? cli_name(&me) : cli_name(cli_serv(c2ptr)->up);
        }
        else
        {
          n2 = host;
          n2up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
        }
        if (c3ptr)
        {
          n3 = cli_name(c3ptr);
          n3up = MyConnect(c3ptr) ? cli_name(&me) : cli_name(cli_serv(c3ptr)->up);
        }
        else
        {
          n3 = host;
          n3up = IsServer(sptr) ? cli_name(sptr) : cli_name(&me);
        }
        if (strcmp(n2, n2up) > 0)
          n2 = n2up;
        if (strcmp(n3, n3up) > 0)
          n3 = n3up;
        if (strcmp(n3, n2) > 0)
        {
          ac2ptr = c2ptr;
          c2ptr = c3ptr;
          c3ptr = ac2ptr;
        }
      }
      /* Now squit the second youngest link: */
      if (!c2ptr)
        return exit_new_server(cptr, sptr, host, timestamp,
                               "server %s already exists and is %ld seconds younger.",
                               host, (long)cli_serv(acptr)->timestamp - (long)timestamp);
      else if (cli_from(c2ptr) == cptr || IsServer(sptr))
      {
        struct Client *killedptrfrom = cli_from(c2ptr);
        if (active_lh_line != ALLOWED)
        {
          /*
           * If the L: or H: line also gets rid of this link,
           * we sent just one squit.
           */
          if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
            break;
          /*
           * If breaking the loop here solves the L: or H:
           * line problem, we don't squit that.
           */
          if (cli_from(c2ptr) == cptr || (LHcptr && a_kills_b_too(c2ptr, LHcptr)))
            active_lh_line = ALLOWED;
          else
          {
            /*
             * If we still have a L: or H: line problem,
             * we prefer to squit the new server, solving
             * loop and L:/H: line problem with only one squit.
             */
            LHcptr = 0;
            break;
          }
        }
        /*
         * If the new server was introduced by a server that caused a
         * Ghost less then 20 seconds ago, this is probably also
         * a Ghost... (20 seconds is more then enough because all
         * SERVER messages are at the beginning of a net.burst). --Run
         */
        if (CurrentTime - cli_serv(cptr)->ghost < 20)
        {
          killedptrfrom = cli_from(acptr);
          if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
            return CPTR_KILLED;
        }
        else if (exit_client_msg(cptr, c2ptr, &me,
            "Loop <-- %s (new link is %ld seconds younger)", host,
            (c3ptr ? (long)cli_serv(c3ptr)->timestamp : timestamp) -
            (long)cli_serv(c2ptr)->timestamp) == CPTR_KILLED)
          return CPTR_KILLED;
        /*
         * Did we kill the incoming server off already ?
         */
        if (killedptrfrom == cptr)
          return 0;
      }
      else
      {
        if (active_lh_line != ALLOWED)
        {
          if (LHcptr && a_kills_b_too(LHcptr, acptr))
            break;
          if (cli_from(acptr) == cptr || (LHcptr && a_kills_b_too(acptr, LHcptr)))
            active_lh_line = ALLOWED;
          else
          {
            LHcptr = 0;
            break;
          }
        }
        /*
         * We can't believe it is a lagged server message
         * when it directly connects to us...
         * kill the older link at the ghost, rather then
         * at the second youngest link, assuming it isn't
         * a REAL loop.
         */
        if (ghost)
          *ghost = CurrentTime;            /* Mark that it caused a ghost */
        if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
          return CPTR_KILLED;
        break;
      }
    }
  }

  if (active_lh_line != ALLOWED)
  {
    if (!LHcptr)
      LHcptr = sptr;
    if (active_lh_line == MAX_HOPS_EXCEEDED)
    {
      return exit_client_msg(cptr, LHcptr, &me,
                             "Maximum hops exceeded for %s at %s",
                             cli_name(cptr), host);
    }
    else if (active_lh_line == NOT_ALLOWED_TO_HUB)
    {
      return exit_client_msg(cptr, LHcptr, &me,
                             "%s is not allowed to hub for %s",
                             cli_name(cptr), host);
    }
    else /* I_AM_NOT_HUB */
    {
      ServerStats->is_ref++;
      return exit_client(cptr, LHcptr, &me, "I'm a leaf, define HUB");
    }
  }

  return 1;
}
Beispiel #25
0
/*
 * ms_zline - server message handler
 *
 * parv[0] = Sender prefix
 * parv[1] = Target: server numeric
 * parv[2] = (+|-)<Z-line mask>
 *
 * For other parameters, see doc/readme.zline.
 */
int
ms_zline(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Client *acptr = 0;
  struct Zline *azline = 0;
  unsigned int flags = 0;
  enum ZlineAction action = ZLINE_MODIFY;
  time_t expire = 0, lastmod = 0, lifetime = 0;
  char *mask = parv[2], *target = parv[1], *reason = "No reason", *tmp = 0;

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

  if (IsServer(sptr))
    flags |= ZLINE_FORCE;

  if (*mask == '!') {
    mask++;
    flags |= ZLINE_OPERFORCE; /* assume oper had WIDE_ZLINE */
  }

  switch (*mask) { /* handle +, -, <, and > */
  case '+': /* activate the ZG-line */
    action = ZLINE_ACTIVATE;
    mask++;
    break;

  case '-': /* deactivate the Z-line */
    action = ZLINE_DEACTIVATE;
    mask++;
    break;

  case '>': /* locally activate the Z-line */
    action = ZLINE_LOCAL_ACTIVATE;
    mask++;
    break;

  case '<': /* locally deactivate the Z-line */
    action = ZLINE_LOCAL_DEACTIVATE;
    mask++;
    break;
  }

  /* Is there no mask left? */
  if (mask[0] == '\0')
    return need_more_params(sptr, "ZLINE");

  /* Now, let's figure out if it's a local or global Z-line */
  if (action == ZLINE_LOCAL_ACTIVATE || action == ZLINE_LOCAL_DEACTIVATE ||
      (target[0] == '*' && target[1] == '\0'))
    flags |= ZLINE_GLOBAL;
  else
    flags |= ZLINE_LOCAL;

  /* now figure out if we need to resolve a server */
  if ((action == ZLINE_LOCAL_ACTIVATE || action == ZLINE_LOCAL_DEACTIVATE ||
       (flags & ZLINE_LOCAL)) && !(acptr = FindNServer(target)))
    return 0; /* no such server, jump out */

  /* If it's a local activate/deactivate and server isn't me, propagate it */
  if ((action == ZLINE_LOCAL_ACTIVATE || action == ZLINE_LOCAL_DEACTIVATE) &&
      !IsMe(acptr)) {
    Debug((DEBUG_DEBUG, "I am forwarding a local change to a global zline "
	   "to a remote server; target %s, mask %s, operforce %s, action %c",
	   target, mask, flags & ZLINE_OPERFORCE ? "YES" : "NO",
	   action == ZLINE_LOCAL_ACTIVATE ? '>' : '<'));

    sendcmdto_one(sptr, CMD_ZLINE, acptr, "%C %s%c%s", acptr,
		  flags & ZLINE_OPERFORCE ? "!" : "",
		  action == ZLINE_LOCAL_ACTIVATE ? '>' : '<', mask);

    return 0; /* all done */
  }

  /* Next, try to find the Z-line... */
  if ((flags & ZLINE_GLOBAL) || IsMe(acptr)) /* don't bother if it's not me! */
    azline = zline_find(mask, flags | ZLINE_ANY | ZLINE_EXACT);

  /* We now have all the pieces to tell us what we've got; let's put
   * it all together and convert the rest of the arguments.
   */

  /* Handle the local Z-lines first... */
  if (flags & ZLINE_LOCAL) {
    assert(acptr);

    /* normalize the action, first */
    if (action == ZLINE_LOCAL_ACTIVATE || action == ZLINE_MODIFY)
      action = ZLINE_ACTIVATE;
    else if (action == ZLINE_LOCAL_DEACTIVATE)
      action = ZLINE_DEACTIVATE;

    if (action == ZLINE_ACTIVATE) { /* get expiration and reason */
      if (parc < 5) /* check parameter count... */
	return need_more_params(sptr, "ZLINE");

      expire = atoi(parv[3]); /* get expiration... */
      expire = abs_expire(expire); /* convert to absolute... */
      reason = parv[parc - 1]; /* and reason */

      if (IsMe(acptr)) {
	if (azline) /* Z-line already exists, so let's ignore it... */
	  return 0;

	/* OK, create the local Z-line */
	Debug((DEBUG_DEBUG, "I am creating a local Z-line here; target %s, "
	       "mask %s, operforce %s, action %s, expire %Tu, reason: %s",
	       target, mask, flags & ZLINE_OPERFORCE ? "YES" : "NO",
	       action == ZLINE_ACTIVATE ? "+" : "-", expire, reason));

	return zline_add(cptr, sptr, mask, reason, expire, lastmod,
			 lifetime, flags | ZLINE_ACTIVE);
      }
    } else if (IsMe(acptr)) { /* destroying a local Z-line */
      if (!azline) /* Z-line doesn't exist, so let's complain... */
	return send_reply(sptr, ERR_NOSUCHZLINE, mask);

      /* Let's now destroy the Z-line */;
      Debug((DEBUG_DEBUG, "I am destroying a local Z-line here; target %s, "
	     "mask %s, operforce %s, action %s", target, mask,
	     flags & ZLINE_OPERFORCE ? "YES" : "NO",
	     action == ZLINE_ACTIVATE ? "+" : "-"));

      return zline_destroy(cptr, sptr, azline);
    }

    /* OK, we've converted arguments; if it's not for us, forward */
    /* UPDATE NOTE: Once all servers are updated to u2.10.12.11, the
     * format string in this sendcmdto_one() may be updated to omit
     * <lastmod> for ZLINE_ACTIVATE and to omit <expire>, <lastmod>,
     * and <reason> for ZLINE_DEACTIVATE.
     */
    assert(!IsMe(acptr));

    Debug((DEBUG_DEBUG, "I am forwarding a local Z-line to a remote server; "
	   "target %s, mask %s, operforce %s, action %c, expire %Tu, "
	   "lastmod %Tu, reason: %s", target, mask,
	   flags & ZLINE_OPERFORCE ? "YES" : "NO",
	   action == ZLINE_ACTIVATE ? '+' :  '-', expire, TStime(),
	   reason));

    sendcmdto_one(sptr, CMD_ZLINE, acptr, "%C %s%c%s %Tu %Tu :%s",
		  acptr, flags & ZLINE_OPERFORCE ? "!" : "",
		  action == ZLINE_ACTIVATE ? '+' : '-', mask,
		  expire - TStime(), TStime(), reason);

    return 0; /* all done */
  }

  /* can't modify a Z-line that doesn't exist, so remap to activate */
  if (!azline && action == ZLINE_MODIFY)
    action = ZLINE_ACTIVATE;

  /* OK, let's figure out what other parameters we may have... */
  switch (action) {
  case ZLINE_LOCAL_ACTIVATE: /* locally activating a Z-line */
  case ZLINE_LOCAL_DEACTIVATE: /* locally deactivating a Z-line */
    if (!azline) /* no Z-line to locally activate or deactivate? */
      return send_reply(sptr, ERR_NOSUCHZLINE, mask);
    lastmod = azline->zl_lastmod;
    break; /* no additional parameters to manipulate */

  case ZLINE_ACTIVATE: /* activating a Z-line */
  case ZLINE_DEACTIVATE: /* deactivating a Z-line */
    /* in either of these cases, we have at least a lastmod parameter */
    if (parc < 4)
      return need_more_params(sptr, "ZLINE");
    else if (parc == 4) /* lastmod only form... */
      lastmod = atoi(parv[3]);
    /*FALLTHROUGH*/
  case ZLINE_MODIFY: /* modifying a Z-line */
    /* convert expire and lastmod, look for lifetime and reason */
    if (parc > 4) { /* protect against fall-through from 4-param form */
      expire = atoi(parv[3]); /* convert expiration and lastmod */
      expire = abs_expire(expire);
      lastmod = atoi(parv[4]);

      flags |= ZLINE_EXPIRE; /* we have an expiration time update */

      if (parc > 6) { /* no question, have a lifetime and reason */
	lifetime = atoi(parv[5]);
	reason = parv[parc - 1];

	flags |= ZLINE_LIFETIME | ZLINE_REASON;
      } else if (parc == 6) { /* either a lifetime or a reason */
	if (!azline || /* zline creation, has to be the reason */
	    /* trial-convert as lifetime, and if it doesn't fully convert,
	     * it must be the reason */
	    (!(lifetime = strtoul(parv[5], &tmp, 10)) && !*tmp)) {
	  lifetime = 0;
	  reason = parv[5];

	  flags |= ZLINE_REASON; /* have a reason update */
	} else if (lifetime)
	  flags |= ZLINE_LIFETIME; /* have a lifetime update */
      }
    }
  }

  if (!lastmod) /* must have a lastmod parameter by now */
    return need_more_params(sptr, "ZLINE");

  Debug((DEBUG_DEBUG, "I have a global Z-line I am acting upon now; "
	 "target %s, mask %s, operforce %s, action %s, expire %Tu, "
	 "lastmod %Tu, lifetime %Tu, reason: %s; zline %s!  (fields "
	 "present: %s %s %s)", target, mask,
	 flags & ZLINE_OPERFORCE ? "YES" : "NO",
	 action == ZLINE_ACTIVATE ? "+" :
	 (action == ZLINE_DEACTIVATE ? "-" :
	  (action == ZLINE_LOCAL_ACTIVATE ? ">" :
	   (action == ZLINE_LOCAL_DEACTIVATE ? "<" : "(MODIFY)"))),
	 expire, lastmod, lifetime, reason,
	 azline ? "EXISTS" : "does not exist",
	 flags & ZLINE_EXPIRE ? "expire" : "",
	 flags & ZLINE_LIFETIME ? "lifetime" : "",
	 flags & ZLINE_REASON ? "reason" : ""));

  /* OK, at this point, we have converted all available parameters.
   * Let's actually do the action!
   */
  if (azline)
    return zline_modify(cptr, sptr, azline, action, reason, expire,
			lastmod, lifetime, flags);

  assert(action != ZLINE_LOCAL_ACTIVATE);
  assert(action != ZLINE_LOCAL_DEACTIVATE);
  assert(action != ZLINE_MODIFY);

  if (!expire) { /* Cannot *add* a Z-line we don't have, but try hard */
    Debug((DEBUG_DEBUG, "Propagating Z-line %s for Z-line we don't have",
	   action == ZLINE_ACTIVATE ? "activation" : "deactivation"));

    /* propagate the Z-line, even though we don't have it */
    sendcmdto_serv_butone(sptr, CMD_ZLINE, cptr, "* %c%s %Tu",
			  action == ZLINE_ACTIVATE ? '+' : '-',
			  mask, lastmod);

    return 0;
  }

  return zline_add(cptr, sptr, mask, reason, expire, lastmod, lifetime,
		   flags | ((action == ZLINE_ACTIVATE) ? ZLINE_ACTIVE : 0));
}
/*
 * ms_burst - server message handler
 *
 * --  by Run [email protected]  december 1995 till march 1997
 *
 * parv[0] = sender prefix
 * parv[1] = channel name
 * parv[2] = channel timestamp
 * The meaning of the following parv[]'s depend on their first character:
 * If parv[n] starts with a '+':
 * Net burst, additive modes
 *   parv[n] = <mode>
 *   parv[n+1] = <param> (optional)
 *   parv[n+2] = <param> (optional)
 * If parv[n] starts with a '%', then n will be parc-1:
 *   parv[n] = %<ban> <ban> <ban> ...
 * If parv[n] starts with another character:
 *   parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
 *   where <mode> is the channel mode (ov) of nick and all following nicks.
 *
 * Example:
 * "S BURST #channel 87654321 +ntkl key 123 AAA,AAB:o,BAA,BAB:ov :%ban1 ban2"
 *
 * Anti net.ride code.
 *
 * When the channel already exist, and its TS is larger then
 * the TS in the BURST message, then we cancel all existing modes.
 * If its is smaller then the received BURST message is ignored.
 * If it's equal, then the received modes are just added.
 */
int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct ModeBuf modebuf, *mbuf = 0;
  struct Channel *chptr;
  time_t timestamp;
  struct Membership *member, *nmember;
  struct SLink *lp, **lp_p;
  unsigned int parse_flags = (MODE_PARSE_FORCE | MODE_PARSE_BURST);
  int param, nickpos = 0, banpos = 0;
  char modestr[BUFSIZE], nickstr[BUFSIZE], banstr[BUFSIZE];

  if (parc < 3)
    return protocol_violation(sptr,"Too few parameters for BURST");

  if (!IsBurst(sptr)) /* don't allow BURST outside of burst */
    return exit_client_msg(cptr, cptr, &me, "HACK: BURST message outside "
			   "net.burst from %s", cli_name(sptr));

  if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
    return 0; /* can't create the channel? */

  timestamp = atoi(parv[2]);

  if (!chptr->creationtime || chptr->creationtime > timestamp) {
    /*
     * Kick local members if channel is +i or +k and our TS was larger
     * than the burst TS (anti net.ride). The modes hack is here because
     * we have to do this before mode_parse, as chptr may go away.
     */
    for (param = 3; param < parc; param++) {
      if (parv[param][0] != '+')
        continue;
      if (strchr(parv[param], 'i') || strchr(parv[param], 'k')) {
        /* Clear any outstanding rogue invites */
        mode_invite_clear(chptr);
        for (member = chptr->members; member; member = nmember) {
          nmember=member->next_member;
          if (!MyUser(member->user) || IsZombie(member))
            continue;
          sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Net Rider", chptr, member->user);
          sendcmdto_channel_butserv_butone(&me, CMD_KICK, chptr, NULL, "%H %C :Net Rider", chptr, member->user);
          make_zombie(member, member->user, &me, &me, chptr);
        }
      }
      break;
    }

    /* If the channel had only locals, it went away by now. */
    if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
      return 0; /* can't create the channel? */
  }

  /* turn off burst joined flag */
  for (member = chptr->members; member; member = member->next_member)
    member->status &= ~CHFL_BURST_JOINED;

  if (!chptr->creationtime) /* mark channel as created during BURST */
    chptr->mode.mode |= MODE_BURSTADDED;

  /* new channel or an older one */
  if (!chptr->creationtime || chptr->creationtime > timestamp) {
    chptr->creationtime = timestamp;

    modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
		 MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
    modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */
    chptr->mode.mode &= ~(MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET |
			  MODE_MODERATED | MODE_TOPICLIMIT | MODE_INVITEONLY |
			  MODE_NOPRIVMSGS);

    parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */

    /* mark bans for wipeout */
    for (lp = chptr->banlist; lp; lp = lp->next)
      lp->flags |= CHFL_BURST_BAN_WIPEOUT;
  
    /* clear topic set by netrider (if set) */
    if (*chptr->topic) {
      *chptr->topic = '\0';
      *chptr->topic_nick = '\0';
      chptr->topic_time = 0;
      sendcmdto_channel_butserv_butone(&me, CMD_TOPIC, chptr, NULL, 
                                       "%H :%s", chptr, chptr->topic);
    }
  } else if (chptr->creationtime == timestamp) {
    modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
		 MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);

    parse_flags |= MODE_PARSE_SET; /* set new modes */
  }

  param = 3; /* parse parameters */
  while (param < parc) {
    switch (*parv[param]) {
    case '+': /* parameter introduces a mode string */
      param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
			  parv + param, parse_flags);
      break;

    case '%': /* parameter contains bans */
      if (parse_flags & MODE_PARSE_SET) {
	char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
	struct SLink *newban;

	for (ban = ircd_strtok(&p, banlist, " "); ban;
	     ban = ircd_strtok(&p, 0, " ")) {
	  ban = collapse(pretty_mask(ban));

	    /*
	     * Yeah, we should probably do this elsewhere, and make it better
	     * and more general; this will hold until we get there, though.
	     * I dislike the current add_banid API... -Kev
	     *
	     * I wish there were a better algo. for this than the n^2 one
	     * shown below *sigh*
	     */
	  for (lp = chptr->banlist; lp; lp = lp->next) {
	    if (!ircd_strcmp(lp->value.ban.banstr, ban)) {
	      ban = 0; /* don't add ban */
	      lp->flags &= ~CHFL_BURST_BAN_WIPEOUT; /* not wiping out */
	      break; /* new ban already existed; don't even repropagate */
	    } else if (!(lp->flags & CHFL_BURST_BAN_WIPEOUT) &&
		       !mmatch(lp->value.ban.banstr, ban)) {
	      ban = 0; /* don't add ban unless wiping out bans */
	      break; /* new ban is encompassed by an existing one; drop */
	    } else if (!mmatch(ban, lp->value.ban.banstr))
	      lp->flags |= CHFL_BAN_OVERLAPPED; /* remove overlapping ban */

	    if (!lp->next)
	      break;
	  }

	  if (ban) { /* add the new ban to the end of the list */
	    /* Build ban buffer */
	    if (!banpos) {
	      banstr[banpos++] = ' ';
	      banstr[banpos++] = ':';
	      banstr[banpos++] = '%';
	    } else
	      banstr[banpos++] = ' ';
	    for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
	      banstr[banpos++] = *ptr;

	    newban = make_link(); /* create new ban */

	    DupString(newban->value.ban.banstr, ban);

	    DupString(newban->value.ban.who, 
		      cli_name(feature_bool(FEAT_HIS_BANWHO) ? &me : sptr));

	    newban->value.ban.when = TStime();

	    newban->flags = CHFL_BAN | CHFL_BURST_BAN; /* set flags */
	    if ((ptr = strrchr(ban, '@')) && check_if_ipmask(ptr + 1))
	      newban->flags |= CHFL_BAN_IPMASK;

	    newban->next = 0;
	    if (lp)
	      lp->next = newban; /* link it in */
	    else
	      chptr->banlist = newban;
	  }
	}
      } 
      param++; /* look at next param */
      break;

    default: /* parameter contains clients */
      {
	struct Client *acptr;
	char *nicklist = parv[param], *p = 0, *nick, *ptr;
	int default_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
	int last_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;

	for (nick = ircd_strtok(&p, nicklist, ","); nick;
	     nick = ircd_strtok(&p, 0, ",")) {

	  if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
	    *ptr++ = '\0';

	    if (parse_flags & MODE_PARSE_SET) {
	      for (default_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; *ptr;
		   ptr++) {
		if (*ptr == 'o') /* has oper status */
		  default_mode = (default_mode & ~CHFL_DEOPPED) | CHFL_CHANOP;
		else if (*ptr == 'v') /* has voice status */
		  default_mode |= CHFL_VOICE;
		else /* I don't recognize that flag */
		  break; /* so stop processing */
	      }
	    }
	  }

	  if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
	    continue; /* ignore this client */

	  /* Build nick buffer */
	  nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
	  nickpos++;

	  for (ptr = nick; *ptr; ptr++) /* store nick */
	    nickstr[nickpos++] = *ptr;

	  if (default_mode != last_mode) { /* if mode changed... */
	    last_mode = default_mode;

	    nickstr[nickpos++] = ':'; /* add a specifier */
	    if (default_mode & CHFL_CHANOP)
	      nickstr[nickpos++] = 'o';
	    if (default_mode & CHFL_VOICE)
	      nickstr[nickpos++] = 'v';
	  }

	  add_user_to_channel(chptr, acptr, default_mode);
	  sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr);
	}
      }
      param++;
      break;
    } /* switch (*parv[param]) { */
  } /* while (param < parc) { */

  nickstr[nickpos] = '\0';
  banstr[banpos] = '\0';

  if (parse_flags & MODE_PARSE_SET) {
    modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
    modestr[0] = modestr[1] ? ' ' : '\0';
  } else
    modestr[0] = '\0';

  sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
			chptr->creationtime, modestr, nickstr, banstr);

  if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
    mode_ban_invalidate(chptr);

  if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
    /* first deal with channel members */
    for (member = chptr->members; member; member = member->next_member) {
      if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
	if (member->status & CHFL_CHANOP)
	  modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user);
	if (member->status & CHFL_VOICE)
	  modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user);
      } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
	if (member->status & CHFL_CHANOP)
	  modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user);
	if (member->status & CHFL_VOICE)
	  modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user);
	member->status = ((member->status & ~(CHFL_CHANOP | CHFL_VOICE)) |
			  CHFL_DEOPPED);
      }
    }

    /* Now deal with channel bans */
    lp_p = &chptr->banlist;
    while (*lp_p) {
      lp = *lp_p;

      /* remove ban from channel */
      if (lp->flags & (CHFL_BAN_OVERLAPPED | CHFL_BURST_BAN_WIPEOUT)) {
	modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
			    lp->value.ban.banstr, 1); /* let it free banstr */

	*lp_p = lp->next; /* clip out of list */
	MyFree(lp->value.ban.who); /* free who */
	free_link(lp); /* free ban */
	continue;
      } else if (lp->flags & CHFL_BURST_BAN) /* add ban to channel */
	modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
			    lp->value.ban.banstr, 0); /* don't free banstr */

      lp->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* reset the flag */
      lp_p = &(*lp_p)->next;
    }
  }

  return mbuf ? modebuf_flush(mbuf) : 0;
}
Beispiel #27
0
/*
 * ms_burst - server message handler
 *
 * --  by Run [email protected]  december 1995 till march 1997
 *
 * parv[0] = sender prefix
 * parv[1] = channel name
 * parv[2] = channel timestamp
 * The meaning of the following parv[]'s depend on their first character:
 * If parv[n] starts with a '+':
 * Net burst, additive modes
 *   parv[n] = <mode>
 *   parv[n+1] = <param> (optional)
 *   parv[n+2] = <param> (optional)
 * If parv[n] starts with a '%', then n will be parc-1:
 *   parv[n] = %<ban> <ban> <ban> ...
 * If parv[n] starts with another character:
 *   parv[n] = <nick>[:<mode>],<nick>[:<mode>],...
 *   where <mode> defines the mode and op-level
 *   for nick and all following nicks until the
 *   next <mode> field.
 *   Digits in the <mode> field have of two meanings:
 *   1) if it is the first field in this BURST message
 *      that contains digits, and/or when a 'v' is
 *      present in the <mode>:
 *      The absolute value of the op-level.
 *   2) if there are only digits in this field and
 *      it is not the first field with digits:
 *      An op-level increment relative to the previous
 *      op-level.
 *   First all modeless nicks must be emmitted,
 *   then all combinations of modes without ops
 *   (currently that is only 'v') followed by the same
 *   series but then with ops (currently 'o','ov').
 *
 * Example:
 * "A8 B #test 87654321 +ntkAl key secret 123 A8AAG,A8AAC:v,A8AAA:0,A8AAF:2,A8AAD,A8AAB:v1,A8AAE:1 :%ban1 ban2"
 *
 * <mode> list example:
 *
 * "xxx,sss:v,ttt,aaa:123,bbb,ccc:2,ddd,kkk:v2,lll:2,mmm"
 *
 * means
 *
 *  xxx		// first modeless nicks
 *  sss +v	// then opless nicks
 *  ttt +v	// no ":<mode>": everything stays the same
 *  aaa -123	// first field with digit: absolute value
 *  bbb -123
 *  ccc -125	// only digits, not first field: increment
 *  ddd -125
 *  kkk -2 +v	// field with a 'v': absolute value
 *  lll -4 +v	// only digits: increment
 *  mmm -4 +v
 *
 * Anti net.ride code.
 *
 * When the channel already exist, and its TS is larger than
 * the TS in the BURST message, then we cancel all existing modes.
 * If its is smaller then the received BURST message is ignored.
 * If it's equal, then the received modes are just added.
 *
 * BURST is also accepted outside a netburst now because it
 * is sent upstream as reaction to a DESTRUCT message.  For
 * these BURST messages it is possible that the listed channel
 * members are already joined.
 */
int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct ModeBuf modebuf, *mbuf = 0;
  struct Channel *chptr;
  time_t timestamp;
  struct Membership *member, *nmember;
  struct Ban *lp, **lp_p;
  unsigned int parse_flags = (MODE_PARSE_FORCE | MODE_PARSE_BURST);
  int param, nickpos = 0, banpos = 0;
  char modestr[BUFSIZE], nickstr[BUFSIZE], banstr[BUFSIZE];

  if (parc < 3)
    return protocol_violation(sptr,"Too few parameters for BURST");

  if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
    return 0; /* can't create the channel? */

  timestamp = atoi(parv[2]);

  if (chptr->creationtime)	/* 0 for new (empty) channels,
                                   i.e. when this server just restarted. */
  {
    if (parc == 3)		/* Zannel BURST? */
    {
      /* An empty channel without +A set, will cause a BURST message
	 with exactly 3 parameters (because all modes have been reset).
	 If the timestamp on such channels is only a few seconds older
	 from our own, then we ignore this burst: we do not deop our
	 own side.
	 Likewise, we expect the other (empty) side to copy our timestamp
	 from our own BURST message, even though it is slightly larger.

	 The reason for this is to allow people to join an empty
	 non-A channel (a zannel) during a net.split, and not be
	 deopped when the net reconnects (with another zannel). When
	 someone joins a split zannel, their side increments the TS by one.
	 If they cycle a few times then we still don't have a reason to
	 deop them. Theoretically I see no reason not to accept ANY timestamp,
	 but to be sure, we only accept timestamps that are just a few
	 seconds off (one second for each time they cycled the channel). */

      /* Don't even deop users who cycled four times during the net.break. */
      if (timestamp < chptr->creationtime &&
          chptr->creationtime <= timestamp + 4 &&
	  chptr->users != 0)	/* Only do this when WE have users, so that
	  			   if we do this the BURST that we sent has
				   parc > 3 and the other side will use the
				   test below: */
	timestamp = chptr->creationtime; /* Do not deop our side. */
    }
    else if (chptr->creationtime < timestamp &&
             timestamp <= chptr->creationtime + 4 &&
	     chptr->users == 0)
    {
      /* If one side of the net.junction does the above
         timestamp = chptr->creationtime, then the other
	 side must do this: */
      chptr->creationtime = timestamp;	/* Use the same TS on both sides. */
    }
    /* In more complex cases, we might still end up with a
       creationtime desync of a few seconds, but that should
       be synced automatically rather quickly (every JOIN
       caries a timestamp and will sync it; modes by users do
       not carry timestamps and are accepted regardless).
       Only when nobody joins the channel on the side with
       the oldest timestamp before a new net.break occurs
       precisely inbetween the desync, an unexpected bounce
       might happen on reconnect. */
  }

  if (!chptr->creationtime || chptr->creationtime > timestamp) {
    /*
     * Kick local members if channel is +i or +k and our TS was larger
     * than the burst TS (anti net.ride). The modes hack is here because
     * we have to do this before mode_parse, as chptr may go away.
     */
    for (param = 3; param < parc; param++)
    {
      int check_modes;
      if (parv[param][0] != '+')
        continue;
      check_modes = netride_modes(parc - param, parv + param, chptr->mode.key);
      if (check_modes < 0)
      {
        if (chptr->users == 0)
          sub1_from_channel(chptr);
        return protocol_violation(sptr, "Invalid mode string in BURST");
      }
      else if (check_modes)
      {
        /* Clear any outstanding rogue invites */
        mode_invite_clear(chptr);
        for (member = chptr->members; member; member = nmember)
        {
          nmember = member->next_member;
          if (!MyUser(member->user) || IsZombie(member))
            continue;
          /* Kick as netrider if key mismatch *or* remote channel is
           * +i (unless user is an oper) *or* remote channel is +r
           * (unless user has an account).
           */
          if (!(check_modes & MODE_KEY)
              && (!(check_modes & MODE_INVITEONLY) || IsAnOper(member->user))
              && (!(check_modes & MODE_REGONLY) || IsAccount(member->user)))
            continue;
          sendcmdto_serv_butone(&me, CMD_KICK, NULL, "%H %C :Net Rider", chptr, member->user);
          sendcmdto_channel_butserv_butone(&his, CMD_KICK, chptr, NULL, 0, "%H %C :Net Rider", chptr, member->user);
          make_zombie(member, member->user, &me, &me, chptr);
        }
      }
      break;
    }

    /* If the channel had only locals, it went away by now. */
    if (!(chptr = get_channel(sptr, parv[1], CGT_CREATE)))
      return 0; /* can't create the channel? */
  }

  /* turn off burst joined flag */
  for (member = chptr->members; member; member = member->next_member)
    member->status &= ~(CHFL_BURST_JOINED|CHFL_BURST_ALREADY_OPPED|CHFL_BURST_ALREADY_VOICED);

  if (!chptr->creationtime) /* mark channel as created during BURST */
    chptr->mode.mode |= MODE_BURSTADDED;

  /* new channel or an older one */
  if (!chptr->creationtime || chptr->creationtime > timestamp) {
    chptr->creationtime = timestamp;

    modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
		 MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);
    modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */
    chptr->mode.mode &= MODE_BURSTADDED | MODE_WASDELJOINS;

    /* wipe out modes not represented in chptr->mode.mode */
    if (chptr->mode.limit) {
      modebuf_mode_uint(mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit);
      chptr->mode.limit = 0;
    }
    if (chptr->mode.key[0]) {
      modebuf_mode_string(mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0);
      chptr->mode.key[0] = '\0';
    }
    if (chptr->mode.upass[0]) {
      modebuf_mode_string(mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0);
      chptr->mode.upass[0] = '\0';
    }
    if (chptr->mode.apass[0]) {
      modebuf_mode_string(mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0);
      chptr->mode.apass[0] = '\0';
    }

    parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */

    /* mark bans for wipeout */
    for (lp = chptr->banlist; lp; lp = lp->next)
      lp->flags |= BAN_BURST_WIPEOUT;

    /* clear topic set by netrider (if set) */
    if (*chptr->topic) {
      *chptr->topic = '\0';
      *chptr->topic_nick = '\0';
      chptr->topic_time = 0;
      sendcmdto_channel_butserv_butone(&his, CMD_TOPIC, chptr, NULL, 0,
                                       "%H :%s", chptr, chptr->topic);
    }
  } else if (chptr->creationtime == timestamp) {
    modebuf_init(mbuf = &modebuf, &me, cptr, chptr,
		 MODEBUF_DEST_CHANNEL | MODEBUF_DEST_NOKEY);

    parse_flags |= MODE_PARSE_SET; /* set new modes */
  }

  param = 3; /* parse parameters */
  while (param < parc) {
    switch (*parv[param]) {
    case '+': /* parameter introduces a mode string */
      param += mode_parse(mbuf, cptr, sptr, chptr, parc - param,
			  parv + param, parse_flags, NULL);
      break;

    case '%': /* parameter contains bans */
      if (parse_flags & MODE_PARSE_SET) {
	char *banlist = parv[param] + 1, *p = 0, *ban, *ptr;
	struct Ban *newban;

	for (ban = ircd_strtok(&p, banlist, " "); ban;
	     ban = ircd_strtok(&p, 0, " ")) {
	  ban = collapse(pretty_mask(ban));

	    /*
	     * Yeah, we should probably do this elsewhere, and make it better
	     * and more general; this will hold until we get there, though.
	     * I dislike the current add_banid API... -Kev
	     *
	     * I wish there were a better algo. for this than the n^2 one
	     * shown below *sigh*
	     */
	  for (lp = chptr->banlist; lp; lp = lp->next) {
	    if (!ircd_strcmp(lp->banstr, ban)) {
	      ban = 0; /* don't add ban */
	      lp->flags &= ~BAN_BURST_WIPEOUT; /* not wiping out */
	      break; /* new ban already existed; don't even repropagate */
	    } else if (!(lp->flags & BAN_BURST_WIPEOUT) &&
		       !mmatch(lp->banstr, ban)) {
	      ban = 0; /* don't add ban unless wiping out bans */
	      break; /* new ban is encompassed by an existing one; drop */
	    } else if (!mmatch(ban, lp->banstr))
	      lp->flags |= BAN_OVERLAPPED; /* remove overlapping ban */

	    if (!lp->next)
	      break;
	  }

	  if (ban) { /* add the new ban to the end of the list */
	    /* Build ban buffer */
	    if (!banpos) {
	      banstr[banpos++] = ' ';
	      banstr[banpos++] = ':';
	      banstr[banpos++] = '%';
	    } else
	      banstr[banpos++] = ' ';
	    for (ptr = ban; *ptr; ptr++) /* add ban to buffer */
	      banstr[banpos++] = *ptr;

	    newban = make_ban(ban); /* create new ban */
            strcpy(newban->who, "*");
	    newban->when = TStime();
	    newban->flags |= BAN_BURSTED;
	    newban->next = 0;
	    if (lp)
	      lp->next = newban; /* link it in */
	    else
	      chptr->banlist = newban;
	  }
	}
      } 
      param++; /* look at next param */
      break;

    default: /* parameter contains clients */
      {
	struct Client *acptr;
	char *nicklist = parv[param], *p = 0, *nick, *ptr;
	int current_mode, last_mode, base_mode;
	int oplevel = -1;	/* Mark first field with digits: means the same as 'o' (but with level). */
	int last_oplevel = 0;
	struct Membership* member;

        base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED;
        if (chptr->mode.mode & MODE_DELJOINS)
            base_mode |= CHFL_DELAYED;
        current_mode = last_mode = base_mode;

	for (nick = ircd_strtok(&p, nicklist, ","); nick;
	     nick = ircd_strtok(&p, 0, ",")) {

	  if ((ptr = strchr(nick, ':'))) { /* new flags; deal */
	    *ptr++ = '\0';

	    if (parse_flags & MODE_PARSE_SET) {
	      int current_mode_needs_reset;
	      for (current_mode_needs_reset = 1; *ptr; ptr++) {
		if (*ptr == 'o') { /* has oper status */
		  /*
		   * An 'o' is pre-oplevel protocol, so this is only for
		   * backwards compatibility.  Give them an op-level of
		   * MAXOPLEVEL so everyone can deop them.
		   */
		  oplevel = MAXOPLEVEL;
		  if (current_mode_needs_reset) {
		    current_mode = base_mode;
		    current_mode_needs_reset = 0;
		  }
		  current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
                  /*
                   * Older servers may send XXYYY:ov, in which case we
                   * do not want to use the code for 'v' below.
                   */
                  if (ptr[1] == 'v') {
                    current_mode |= CHFL_VOICE;
                    ptr++;
                  }
		}
		else if (*ptr == 'v') { /* has voice status */
		  if (current_mode_needs_reset) {
                    current_mode = base_mode;
		    current_mode_needs_reset = 0;
		  }
		  current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE;
		  oplevel = -1;	/* subsequent digits are an absolute op-level value. */
                }
		else if (IsDigit(*ptr)) {
		  int level_increment = 0;
		  if (oplevel == -1) { /* op-level is absolute value? */
		    if (current_mode_needs_reset) {
		      current_mode = base_mode;
		      current_mode_needs_reset = 0;
		    }
		    oplevel = 0;
		  }
		  current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP;
		  do {
		    level_increment = 10 * level_increment + *ptr++ - '0';
		  } while (IsDigit(*ptr));
		  oplevel += level_increment;
		}
		else /* I don't recognize that flag */
		  break; /* so stop processing */
	      }
	    }
	  }

	  if (!(acptr = findNUser(nick)) || cli_from(acptr) != cptr)
	    continue; /* ignore this client */

	  /* Build nick buffer */
	  nickstr[nickpos] = nickpos ? ',' : ' '; /* first char */
	  nickpos++;

	  for (ptr = nick; *ptr; ptr++) /* store nick */
	    nickstr[nickpos++] = *ptr;

	  if (current_mode != last_mode) { /* if mode changed... */
	    last_mode = current_mode;
	    last_oplevel = oplevel;

	    nickstr[nickpos++] = ':'; /* add a specifier */
	    if (current_mode & CHFL_VOICE)
	      nickstr[nickpos++] = 'v';
	    if (current_mode & CHFL_CHANOP)
            {
              if (chptr->mode.apass[0])
	        nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel);
              else
                nickstr[nickpos++] = 'o';
            }
	  } else if (current_mode & CHFL_CHANOP && oplevel != last_oplevel) { /* if just op level changed... */
	    nickstr[nickpos++] = ':'; /* add a specifier */
	    nickpos += ircd_snprintf(0, nickstr + nickpos, sizeof(nickstr) - nickpos, "%u", oplevel - last_oplevel);
            last_oplevel = oplevel;
	  }

	  if (!(member = find_member_link(chptr, acptr)))
	  {
	    add_user_to_channel(chptr, acptr, current_mode, oplevel);
            if (!(current_mode & CHFL_DELAYED))
              sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, 0, "%H", chptr);
	  }
	  else
	  {
	    /* The member was already joined (either by CREATE or JOIN).
	       Remember the current mode. */
	    if (member->status & CHFL_CHANOP)
	      member->status |= CHFL_BURST_ALREADY_OPPED;
	    if (member->status & CHFL_VOICE)
	      member->status |= CHFL_BURST_ALREADY_VOICED;
	    /* Synchronize with the burst. */
	    member->status |= CHFL_BURST_JOINED | (current_mode & (CHFL_CHANOP|CHFL_VOICE));
	    SetOpLevel(member, oplevel);
	  }
	}
      }
      param++;
      break;
    } /* switch (*parv[param]) */
  } /* while (param < parc) */

  nickstr[nickpos] = '\0';
  banstr[banpos] = '\0';

  if (parse_flags & MODE_PARSE_SET) {
    modebuf_extract(mbuf, modestr + 1); /* for sending BURST onward */
    modestr[0] = modestr[1] ? ' ' : '\0';
  } else
    modestr[0] = '\0';

  sendcmdto_serv_butone(sptr, CMD_BURST, cptr, "%H %Tu%s%s%s", chptr,
			chptr->creationtime, modestr, nickstr, banstr);

  if (parse_flags & MODE_PARSE_WIPEOUT || banpos)
    mode_ban_invalidate(chptr);

  if (parse_flags & MODE_PARSE_SET) { /* any modes changed? */
    /* first deal with channel members */
    for (member = chptr->members; member; member = member->next_member) {
      if (member->status & CHFL_BURST_JOINED) { /* joined during burst */
	if ((member->status & CHFL_CHANOP) && !(member->status & CHFL_BURST_ALREADY_OPPED))
	  modebuf_mode_client(mbuf, MODE_ADD | CHFL_CHANOP, member->user, OpLevel(member));
	if ((member->status & CHFL_VOICE) && !(member->status & CHFL_BURST_ALREADY_VOICED))
	  modebuf_mode_client(mbuf, MODE_ADD | CHFL_VOICE, member->user, OpLevel(member));
      } else if (parse_flags & MODE_PARSE_WIPEOUT) { /* wipeout old ops */
	if (member->status & CHFL_CHANOP)
	  modebuf_mode_client(mbuf, MODE_DEL | CHFL_CHANOP, member->user, OpLevel(member));
	if (member->status & CHFL_VOICE)
	  modebuf_mode_client(mbuf, MODE_DEL | CHFL_VOICE, member->user, OpLevel(member));
	member->status = (member->status
                          & ~(CHFL_CHANNEL_MANAGER | CHFL_CHANOP | CHFL_VOICE))
			 | CHFL_DEOPPED;
      }
    }

    /* Now deal with channel bans */
    lp_p = &chptr->banlist;
    while (*lp_p) {
      lp = *lp_p;

      /* remove ban from channel */
      if (lp->flags & (BAN_OVERLAPPED | BAN_BURST_WIPEOUT)) {
        char *bandup;
        DupString(bandup, lp->banstr);
	modebuf_mode_string(mbuf, MODE_DEL | MODE_BAN,
			    bandup, 1);
	*lp_p = lp->next; /* clip out of list */
        free_ban(lp);
	continue;
      } else if (lp->flags & BAN_BURSTED) /* add ban to channel */
	modebuf_mode_string(mbuf, MODE_ADD | MODE_BAN,
			    lp->banstr, 0); /* don't free banstr */

      lp->flags &= BAN_IPMASK; /* reset the flag */
      lp_p = &(*lp_p)->next;
    }
  }

  return mbuf ? modebuf_flush(mbuf) : 0;
}
Beispiel #28
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;
}