예제 #1
0
파일: ircd_relay.c 프로젝트: ryden/ircuRH
void server_relay_private_notice(struct Client* sptr, const char* name, const char* text)
{
  struct Client* acptr;
  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);
  /*
   * nickname addressed?
   */
  if (0 == (acptr = findNUser(name)) || !IsUser(acptr))
    return;

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

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

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

  sendcmdto_one(sptr, CMD_NOTICE, acptr, "%C :%s", acptr, text);
}
예제 #2
0
파일: ircd_relay.c 프로젝트: ryden/ircuRH
void server_relay_private_message(struct Client* sptr, const char* name, const char* text)
{
  struct Client* acptr;
  assert(0 != sptr);
  assert(0 != name);
  assert(0 != text);
  /*
   * nickname addressed?
   */
  if (0 == (acptr = findNUser(name)) || !IsUser(acptr)) {
    send_reply(sptr, SND_EXPLICIT | ERR_NOSUCHNICK, "* :Target left %s. "
	       "Failed to deliver: [%.20s]", feature_str(FEAT_NETWORK), text);
    return;
  }

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

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

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

  sendcmdto_one(sptr, CMD_PRIVATE, acptr, "%C :%s", acptr, text);
}
/*
 * ms_fakehost - fakehost server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = target user numeric
 * parv[2] = target user's new fake host
 */
int ms_fakehost(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client *target;

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

  /* Locate our target user; ignore the message if we can't */
  if(!(target = findNUser(parv[1])))
    return 0;

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

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

  sendcmdto_serv_butone(sptr, CMD_FAKEHOST, cptr, "%C %s", target,
                        cli_user(target)->fakehost);
  return 0;
}
예제 #4
0
/*
 * ms_kill - server message handler
 *
 * NOTE: IsServer(cptr) == true;
 *
 * parv[0]      = sender prefix
 * parv[1]      = kill victim
 * parv[parc-1] = kill path
 */
int ms_kill(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client* victim;
  char*          path;
  char*          msg;

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

  /*
   * XXX - a server sending less than 3 params could really desync
   * things
   */
  if (parc < 3) {
    protocol_violation(sptr,"Too few arguments for KILL");
    return need_more_params(sptr, "KILL");
  }

  path = parv[parc - 1];        /* Either defined or NULL (parc >= 3) */

  if (!(msg = strchr(path, ' '))) /* Extract out the message */
    msg = "(No reason supplied)";
  else
    *(msg++) = '\0'; /* Remove first character (space) and terminate path */

  if (!(victim = findNUser(parv[1]))) {
    if (IsUser(sptr))
      sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :KILL target disconnected "
		    "before I got him :(", sptr);
    return 0;
  }

  /*
   * We *can* have crossed a NICK with this numeric... --Run
   *
   * Note the following situation:
   *  KILL SAA -->       X
   *  <-- S NICK ... SAA | <-- SAA QUIT <-- S NICK ... SAA <-- SQUIT S
   * Where the KILL reaches point X before the QUIT does.
   * This would then *still* cause an orphan because the KILL doesn't reach S
   * (because of the SQUIT), the QUIT is ignored (because of the KILL)
   * and the second NICK ... SAA causes an orphan on the server at the
   * right (which then isn't removed when the SQUIT arrives).
   * Therefore we still need to detect numeric nick collisions too.
   *
   * Bounce the kill back to the originator, if the client can't be found
   * by the next hop (short lag) the bounce won't propagate further.
   */
  if (MyConnect(victim)) {
    sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (Ghost 5 Numeric Collided)",
                  victim, path);
  }
  return do_kill(cptr, sptr, victim, cli_name(cptr), path, msg);
}
예제 #5
0
파일: m_rpong.c 프로젝트: jast/ircu-now
/** Handle an RPONG message from a server
 * -- by Run too :)
 *
 * This message is used for reporting the results of remotely
 * initiated pings.  See ms_rping() for the theory of operation.
 *
 * Going from the rping target to the rping source, \a parv has the
 * following elements:
 * \li \a parv[1] is the rping source's name (numnick is also allowed)
 * \li \a parv[2] is the rping requester's numnick
 * \li \a parv[3] is the rping start time (seconds part)
 * \li \a parv[4] is the rping start time (microseconds part)
 * \li \a parv[5] is the requester's remark
 *
 * Going from the rping source to the rping requester, \a parv has the
 * following elements:
 * \li \a parv[1] is the rping source's name (numnick is also allowed)
 * \li \a parv[2] is the rping requester's numnick
 * \li \a parv[3] is the rping round trip time in milliseconds
 * \li \a parv[4] is the requester's remark
 *
 * See @ref m_functions for discussion of the arguments.
 * @param[in] cptr Client that sent us the message.
 * @param[in] sptr Original source of message.
 * @param[in] parc Number of arguments.
 * @param[in] parv Argument vector.
 */
int ms_rpong(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client *acptr;

  if (!IsServer(sptr))
    return 0;

  if (parc < 5) {
    /*
     * PROTOCOL ERROR
     */
    return need_more_params(sptr, "RPONG");
  }
  if (parc == 6) {
    /*
     * from pinged server to source server
     */
    if (!(acptr = FindServer(parv[1])) && !(acptr = FindNServer(parv[1])))
      return 0;
   
    if (IsMe(acptr)) {
      if (!(acptr = findNUser(parv[2])))
        return 0;

      sendcmdto_one(&me, CMD_RPONG, acptr, "%C %s %s :%s", acptr, cli_name(sptr),
		    militime(parv[3], parv[4]), parv[5]);
    } else
      sendcmdto_one(sptr, CMD_RPONG, acptr, "%s %s %s %s :%s", parv[1],
		    parv[2], parv[3], parv[4], parv[5]);
  } else {
    /*
     * returned from source server to client
     */
    if (!(acptr = findNUser(parv[1])))
      return 0;

    sendcmdto_one(sptr, CMD_RPONG, acptr, "%C %s %s :%s", acptr, parv[2],
		  parv[3], parv[4]);
  }
  return 0;
}
예제 #6
0
파일: m_silence.c 프로젝트: Niichan/snircd
/** Handle a SILENCE command from a server.
 * See @ref m_functions for general discussion of parameters.
 *
 * \a parv[1] may be one of the following:
 * \li "*" to indicate a broadcast update (removing a SILENCE)
 * \li A client numnick that should be specifically SILENCEd.
 *
 * \a parv[2] is a comma-separated list of silence updates.
 *
 * @param[in] cptr Client that sent us the message.
 * @param[in] sptr Original source of message.
 * @param[in] parc Number of arguments.
 * @param[in] parv Argument vector.
 */
int ms_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  if (IsServer(sptr))
    return protocol_violation(sptr, "Server trying to silence a user");
  if (parc < 3 || EmptyString(parv[2]))
    return need_more_params(sptr, "SILENCE");

  /* Figure out which silences can be forwarded. */
  forward_silences(sptr, parv[2], findNUser(parv[1]));
  return 0;
  (void)cptr;
}
예제 #7
0
파일: s_numeric.c 프로젝트: DamnIO/DamnIRCd
/** Forwards a numeric message from a remote server.
 * @param numeric Value of numeric message.
 * @param nnn If non-zero, treat parv[1] as a numnick; else as a client name.
 * @param cptr Client that originated the numeric.
 * @param sptr Peer that sent us the numeric.
 * @param parc Count of valid arguments in \a parv.
 * @param parv Argument list.
 * @return Zero (always).
 */
int do_numeric(int numeric, int nnn, struct Client *cptr, struct Client *sptr,
    int parc, char *parv[])
{
  struct Client *acptr = 0;
  struct Channel *achptr = 0;
  char num[4];

  /* Avoid trash, we need it to come from a server and have a target  */
  if ((parc < 2) || !IsServer(sptr))
    return 0;

  /* Who should receive this message ? Will we do something with it ?
     Note that we use findUser functions, so the target can't be neither
     a server, nor a channel (?) nor a list of targets (?) .. u2.10
     should never generate numeric replies to non-users anyway
     Ahem... it can be a channel actually, csc bots use it :\ --Nem */

  if (IsChannelName(parv[1]))
    achptr = FindChannel(parv[1]);
  else
    acptr = (nnn) ? (findNUser(parv[1])) : (FindUser(parv[1]));

  if (((!acptr) || (cli_from(acptr) == cptr)) && !achptr)
    return 0;

  /* Remap low number numerics, not that I understand WHY.. --Nemesi  */
  /* numerics below 100 talk about the current 'connection', you're not
   * connected to a remote server so it doesn't make sense to send them
   * remotely - but the information they contain may be useful, so we
   * remap them up.  Weird, but true.  -- Isomer */
  if (numeric < 100)
    numeric += 100;

  ircd_snprintf(0, num, sizeof(num), "%03d", numeric);

  /* Since 2.10.10.pl14 we rewrite numerics from remote servers to appear to
   * come from the local server
   */
  if (acptr)
    sendcmdto_one((feature_bool(FEAT_HIS_REWRITE) && !IsOper(acptr)) ?
                    &me : sptr,
                  num, num, acptr, "%C %s", acptr, parv[2]);
  else
    sendcmdto_channel_butone(feature_bool(FEAT_HIS_REWRITE) ? &me : sptr,
                             num, num, achptr, cptr, SKIP_DEAF | SKIP_BURST,
                             '\0', "%H %s", achptr, parv[2]);
  return 0;
}
/*
 * 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;
}
예제 #9
0
/*
 * ms_svsident - server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = Target numeric
 * parv[2] = New ident
 */
int ms_svsident(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client *acptr;
  char *s;
  char *newident = NULL;
  int legalident=1;

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

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

  if (IsChannelService(acptr))
    return 0;

  newident = strdup(parv[2]);

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

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

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

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

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

  return 0;
}
예제 #10
0
파일: m_svsinfo.c 프로젝트: DamnIO/DamnIRCd
/*
 * 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;
}
예제 #11
0
/*
 * ms_xreply - extension message reply handler
 *
 * parv[0] = sender prefix
 * parv[1] = target server numeric
 * parv[2] = routing information
 * parv[3] = extension message reply
 */
int ms_xreply(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  struct Client* acptr;
  const char* routing;
  const char* reply;

  if (parc < 4) /* have enough parameters? */
    return need_more_params(sptr, "XREPLY");

  routing = parv[2];
  reply = parv[3];

  /* Look up the target */
  if (!(acptr = findNUser(parv[1])) && !(acptr = FindNServer(parv[1])))
    return send_reply(sptr, SND_EXPLICIT | ERR_NOSUCHSERVER,
		      "* :Server has disconnected");

  /* If it's not to us, forward the reply */
  if (!IsMe(acptr)) {
    sendcmdto_one(sptr, CMD_XREPLY, acptr, "%C %s :%s", acptr, routing,
		  reply);
    return 0;
  }

  /* OK, figure out where to route the message */
  if (!ircd_strncmp("iauth:", routing, 6)) {
    /* Forward the reply to the iauth */
    routing += 6;

    auth_send_xreply(sptr, routing, reply);
  } else
    /* If we don't know where to route it, log it and drop it */
    log_write(LS_SYSTEM, L_NOTICE, 0, "Received unroutable extension reply "
	      "from %#C to %#C routing %s; message: %s", sptr, acptr,
	      routing, reply);

  return 0;
}
예제 #12
0
파일: m_asll.c 프로젝트: Niichan/snircd
/*
 * ms_asll - server message handler
 */
int ms_asll(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
{
  char *mask;
  struct Client *acptr;
  int hits;
  int i;

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

  if (parc > 5) {
    if (!(acptr = findNUser(parv[1])))
      return 0;
    if (MyUser(acptr))
      send_asll_reply(sptr, acptr, parv[2], atoi(parv[3]), atoi(parv[4]), atoi(parv[5]));
    else
      sendcmdto_prio_one(sptr, CMD_ASLL, acptr, "%C %s %s %s %s",
        acptr, parv[2], parv[3], parv[4], parv[5]);
    return 0;
  }

  if (hunt_server_prio_cmd(sptr, CMD_ASLL, cptr, 1, "%s %C", 2, parc, parv) != HUNTED_ISME)
    return 0;
  mask = parv[1];

  for (i = hits = 0; i <= HighestFd; i++) {
    acptr = LocalClientArray[i];
    if (!acptr || !IsServer(acptr) || !MyConnect(acptr) || match(mask, cli_name(acptr)))
      continue;
    sendcmdto_prio_one(&me, CMD_ASLL, sptr, "%C %s %i %i %i", sptr,
      cli_name(acptr), cli_serv(acptr)->asll_rtt,
      cli_serv(acptr)->asll_to, cli_serv(acptr)->asll_from);
    hits++;
  }
  sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :AsLL for %s: %d local servers matched", sptr, mask, hits);
  return 0;
}
예제 #13
0
/*
 * ms_kick - server message handler
 *
 * parv[0] = sender prefix
 * parv[1] = channel
 * parv[2] = client to kick
 * parv[parc-1] = kick comment
 */
int ms_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
  struct Client *who;
  struct Channel *chptr;
  struct Membership *member = 0, *sptr_link = 0;
  char *name, *comment;

  ClrFlag(sptr, FLAG_TS8);

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

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

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

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

  /* Send HACK notice, but not for servers in BURST */
  /* 2002-10-17: Don't send HACK if the users local server is kicking them */
  if (IsServer(sptr) &&
      !IsBurstOrBurstAck(sptr) &&
      sptr!=cli_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;
}
예제 #14
0
파일: s_serv.c 프로젝트: jast/ircu-now
/** Handle a connection that has sent a valid PASS and SERVER.
 * @param cptr New peer server.
 * @param aconf Connect block for \a cptr.
 * @return Zero.
 */
int server_estab(struct Client *cptr, struct ConfItem *aconf)
{
  struct Client* acptr = 0;
  const char*    inpath;
  int            i;

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

  inpath = cli_name(cptr);

  if (IsUnknown(cptr)) {
    if (aconf->passwd[0])
      sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd);
    /*
     *  Pass my info to the new server
     */
    sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s",
		  cli_name(&me), cli_serv(&me)->timestamp,
		  cli_serv(cptr)->timestamp, MAJOR_PROTOCOL, NumServCap(&me),
		  feature_bool(FEAT_HUB) ? "h" : "",
		  *(cli_info(&me)) ? cli_info(&me) : "IRCers United");
  }

  det_confs_butmask(cptr, CONF_SERVER);

  if (!IsHandshake(cptr))
    hAddClient(cptr);
  SetServer(cptr);
  cli_handler(cptr) = SERVER_HANDLER;
  Count_unknownbecomesserver(UserStats);
  SetBurst(cptr);

/*    nextping = CurrentTime; */

  /*
   * NOTE: check for acptr->user == cptr->serv->user is necessary to insure
   * that we got the same one... bleah
   */
  if (cli_serv(cptr)->user && *(cli_serv(cptr))->by &&
      (acptr = findNUser(cli_serv(cptr)->by))) {
    if (cli_user(acptr) == cli_serv(cptr)->user) {
      sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s established.",
                    acptr, inpath);
    }
    else {
      /*
       * if not the same client, set by to empty string
       */
      acptr = 0;
      *(cli_serv(cptr))->by = '\0';
    }
  }

  sendto_opmask(acptr, SNO_OLDSNO, "Link with %s established.", inpath);
  cli_serv(cptr)->up = &me;
  cli_serv(cptr)->updown = add_dlink(&(cli_serv(&me))->down, cptr);
  sendto_opmask(0, SNO_NETWORK, "Net junction: %s %s", cli_name(&me),
                cli_name(cptr));
  SetJunction(cptr);
  /*
   * Old sendto_serv_but_one() call removed because we now
   * need to send different names to different servers
   * (domain name matching) Send new server to other servers.
   */
  for (i = 0; i <= HighestFd; i++)
  {
    if (!(acptr = LocalClientArray[i]) || !IsServer(acptr) ||
        acptr == cptr || IsMe(acptr))
      continue;
    if (!match(cli_name(&me), cli_name(cptr)))
      continue;
    sendcmdto_one(&me, CMD_SERVER, acptr,
		  "%s 2 0 %Tu J%02u %s%s +%s%s%s :%s", cli_name(cptr),
		  cli_serv(cptr)->timestamp, Protocol(cptr), NumServCap(cptr),
		  IsHub(cptr) ? "h" : "", IsService(cptr) ? "s" : "",
		  IsIPv6(cptr) ? "6" : "", cli_info(cptr));
  }

  /* Send these as early as possible so that glined users/juped servers can
   * be removed from the network while the remote server is still chewing
   * our burst.
   */
  gline_burst(cptr);
  jupe_burst(cptr);

  /*
   * Pass on my client information to the new server
   *
   * First, pass only servers (idea is that if the link gets
   * canceled because the server was already there,
   * there are no NICK's to be canceled...). Of course,
   * if cancellation occurs, all this info is sent anyway,
   * and I guess the link dies when a read is attempted...? --msa
   *
   * Note: Link cancellation to occur at this point means
   * that at least two servers from my fragment are building
   * up connection this other fragment at the same time, it's
   * a race condition, not the normal way of operation...
   */

  for (acptr = &me; acptr; acptr = cli_prev(acptr)) {
    /* acptr->from == acptr for acptr == cptr */
    if (cli_from(acptr) == cptr)
      continue;
    if (IsServer(acptr)) {
      const char* protocol_str;

      if (Protocol(acptr) > 9)
        protocol_str = IsBurst(acptr) ? "J" : "P";
      else
        protocol_str = IsBurst(acptr) ? "J0" : "P0";

      if (0 == match(cli_name(&me), cli_name(acptr)))
        continue;
      sendcmdto_one(cli_serv(acptr)->up, CMD_SERVER, cptr,
		    "%s %d 0 %Tu %s%u %s%s +%s%s%s :%s", cli_name(acptr),
		    cli_hopcount(acptr) + 1, cli_serv(acptr)->timestamp,
		    protocol_str, Protocol(acptr), NumServCap(acptr),
		    IsHub(acptr) ? "h" : "", IsService(acptr) ? "s" : "",
		    IsIPv6(acptr) ? "6" : "", cli_info(acptr));
    }
  }

  for (acptr = &me; acptr; acptr = cli_prev(acptr))
  {
    /* acptr->from == acptr for acptr == cptr */
    if (cli_from(acptr) == cptr)
      continue;
    if (IsUser(acptr))
    {
      char xxx_buf[25];
      char *s = umode_str(acptr);
      sendcmdto_one(cli_user(acptr)->server, CMD_NICK, cptr,
		    "%s %d %Tu %s %s %s%s%s%s %s%s :%s",
		    cli_name(acptr), cli_hopcount(acptr) + 1, cli_lastnick(acptr),
		    cli_user(acptr)->username, cli_user(acptr)->realhost,
		    *s ? "+" : "", s, *s ? " " : "",
		    iptobase64(xxx_buf, &cli_ip(acptr), sizeof(xxx_buf), IsIPv6(cptr)),
		    NumNick(acptr), cli_info(acptr));
    }
  }
  /*
   * Last, send the BURST.
   * (Or for 2.9 servers: pass all channels plus statuses)
   */
  {
    struct Channel *chptr;
    for (chptr = GlobalChannelList; chptr; chptr = chptr->next)
      send_channel_modes(cptr, chptr);
  }
  sendcmdto_one(&me, CMD_END_OF_BURST, cptr, "");
  return 0;
}
예제 #15
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;
}
예제 #16
0
/**
 * Exits a client of *any* type (user, server, etc)
 * from this server. Also, this generates all necessary prototol
 * messages that this exit may cause.
 *
 * This function implicitly exits all other clients depending on
 * this connection.
 *
 * For convenience, this function returns a suitable value for
 * m_function return value:
 *
 *   CPTR_KILLED     if (cptr == bcptr)
 *   0                if (cptr != bcptr)
 *
 * This function can be called in two ways:
 * 1) From before or in parse(), exiting the 'cptr', in which case it was
 *    invoked as exit_client(cptr, cptr, &me,...), causing it to always
 *    return CPTR_KILLED.
 * 2) Via parse from a m_function call, in which case it was invoked as
 *    exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client
 *    that generated the message in a way that we can assume he already
 *    did remove acptr from memory himself (or in other cases we don't mind
 *    because he will be delinked.) Or invoked as:
 *    exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should
 *    be removed.
 * In general: No generated SQUIT or QUIT should be sent to source link
 * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too).
 *
 * --Run
 * @param cptr Connection currently being handled by read_message.
 * @param victim Client being killed.
 * @param killer Client that made the decision to remove \a victim.
 * @param comment Reason for the exit.
 * @return CPTR_KILLED if cptr == bcptr, else 0.
 */
int exit_client(struct Client *cptr,
    struct Client* victim,
    struct Client* killer,
    const char* comment)
{
  struct Client* acptr = 0;
  struct DLink *dlp;
  time_t on_for;

  char comment1[HOSTLEN + HOSTLEN + 2];
  assert(killer);
  if (MyConnect(victim))
  {
    SetFlag(victim, FLAG_CLOSING);

    if (feature_bool(FEAT_CONNEXIT_NOTICES) && IsUser(victim))
      sendto_opmask_butone(0, SNO_CONNEXIT,
                           "Client exiting: %s (%s@%s) [%s] [%s] <%s%s>",
                           cli_name(victim), cli_user(victim)->username,
                           cli_user(victim)->host, comment,
                           ircd_ntoa(&cli_ip(victim)),
                           NumNick(victim) /* two %s's */);
    update_load();

    on_for = CurrentTime - cli_firsttime(victim);

    if (IsUser(victim) || IsUserPort(victim))
      auth_send_exit(victim);

    if (IsUser(victim))
      log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s",
		cli_firsttime(victim), on_for,
		cli_user(victim)->username, cli_sockhost(victim),
                ircd_ntoa(&cli_ip(victim)),
                IsAccount(victim) ? cli_username(victim) : "0",
                NumNick(victim), /* two %s's */
                cli_name(victim), cli_info(victim));

    if (victim != cli_from(killer)  /* The source knows already */
        && IsClient(victim))    /* Not a Ping struct or Log file */
    {
      if (IsServer(victim) || IsHandshake(victim))
	sendcmdto_one(killer, CMD_SQUIT, victim, "%s 0 :%s", cli_name(&me), comment);
      else if (!IsConnecting(victim)) {
        if (!IsDead(victim)) {
	  if (IsServer(victim))
	    sendcmdto_one(killer, CMD_ERROR, victim,
			  ":Closing Link: %s by %s (%s)", cli_name(victim),
			  cli_name(killer), comment);
	  else
	    sendrawto_one(victim, MSG_ERROR " :Closing Link: %s by %s (%s)",
			  cli_name(victim),
                          cli_name(IsServer(killer) ? &his : killer),
			  comment);
	}
      }
      if ((IsServer(victim) || IsHandshake(victim) || IsConnecting(victim)) &&
          (killer == &me || (IsServer(killer) &&
          (strncmp(comment, "Leaf-only link", 14) ||
          strncmp(comment, "Non-Hub link", 12)))))
      {
        /*
         * Note: check user == user needed to make sure we have the same
         * client
         */
        if (cli_serv(victim)->user && *(cli_serv(victim))->by &&
            (acptr = findNUser(cli_serv(victim)->by))) {
          if (cli_user(acptr) == cli_serv(victim)->user) {
	    sendcmdto_one(&me, CMD_NOTICE, acptr,
			  "%C :Link with %s canceled: %s", acptr,
			  cli_name(victim), comment);
          }
          else {
            /*
             * not right client, set by to empty string
             */
            acptr = 0;
            *(cli_serv(victim))->by = '\0';
          }
        }
        if (killer == &me)
	  sendto_opmask_butone(acptr, SNO_OLDSNO, "Link with %s canceled: %s",
			       cli_name(victim), comment);
      }
    }
    /*
     *  Close the Client connection first.
     */
    close_connection(victim);
  }

  if (IsServer(victim))
  {
    if (feature_bool(FEAT_HIS_NETSPLIT))
      strcpy(comment1, "*.net *.split");
    else
    {
      strcpy(comment1, cli_name(cli_serv(victim)->up));
      strcat(comment1, " ");
      strcat(comment1, cli_name(victim));
    }

    if (IsUser(killer))
      sendto_opmask_butone(killer, SNO_OLDSNO, "%s SQUIT by %s [%s]:",
			   (cli_user(killer)->server == victim ||
			    cli_user(killer)->server == cli_serv(victim)->up) ?
			   "Local" : "Remote",
			   get_client_name(killer, HIDE_IP),
			   cli_name(cli_user(killer)->server));
    else if (killer != &me && cli_serv(victim)->up != killer)
      sendto_opmask_butone(0, SNO_OLDSNO, "Received SQUIT %s from %s :",
			   cli_name(victim), IsServer(killer) ? cli_name(killer) :
			   get_client_name(killer, HIDE_IP));
    sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)",
			 cli_serv(victim)->up, victim, comment);
  }

  /*
   * First generate the needed protocol for the other server links
   * except the source:
   */
  for (dlp = cli_serv(&me)->down; dlp; dlp = dlp->next) {
    if (dlp->value.cptr != cli_from(killer) && dlp->value.cptr != victim)
    {
      if (IsServer(victim))
	sendcmdto_one(killer, CMD_SQUIT, dlp->value.cptr, "%s %Tu :%s",
		      cli_name(victim), cli_serv(victim)->timestamp, comment);
      else if (IsUser(victim) && !HasFlag(victim, FLAG_KILLED))
	sendcmdto_one(victim, CMD_QUIT, dlp->value.cptr, ":%s", comment);
    }
  }
  /* Then remove the client structures */
  if (IsServer(victim))
    exit_downlinks(victim, killer, comment1);
  exit_one_client(victim, comment);

  /*
   *  cptr can only have been killed if it was cptr itself that got killed here,
   *  because cptr can never have been a dependent of victim    --Run
   */
  return (cptr == victim) ? CPTR_KILLED : 0;
}
예제 #17
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;
}
예제 #18
0
/** Parse a line of data from a server.
 * @param[in] cptr Client that sent the data.
 * @param[in] buffer Start of input line.
 * @param[in] bufend End of input line.
 * @return 0 on success, -1 on parse error, or CPTR_KILLED if message
 * handler returns it.
 */
int parse_server(struct Client *cptr, char *buffer, char *bufend)
{
  struct Client*  from = cptr;
  char*           ch = buffer;
  char*           s;
  int             len;
  int             i;
  int             numeric = 0;
  int             paramcount;
  struct Message* mptr;

  Debug((DEBUG_DEBUG, "Server Parsing: %s", buffer));

  if (IsDead(cptr))
    return 0;

  para[0] = cli_name(from);

  /*
   * A server ALWAYS sends a prefix. When it starts with a ':' it's the
   * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric
   * nick or server
   */
  if (*ch == ':')
  {
    /* Let para[0] point to the name of the sender */
    para[0] = ch + 1;
    if (!(ch = strchr(ch, ' ')))
      return -1;
    *ch++ = '\0';

    /* And let `from' point to its client structure,
       opps.. a server is _also_ a client --Nem */
    from = FindClient(para[0]);

    /*
     * If the client corresponding to the
     * prefix is not found. We must ignore it,
     * it is simply a lagged message traveling
     * upstream a SQUIT that removed the client
     * --Run
     */
    if (from == NULL)
    {
      Debug((DEBUG_NOTICE, "Unknown prefix (%s)(%s) from (%s)",
          para[0], buffer, cli_name(cptr)));
      ++ServerStats->is_unpf;
      while (*ch == ' ')
        ch++;
      /*
       * However, the only thing that MUST be
       * allowed to travel upstream against an
       * squit, is an SQUIT itself (the timestamp
       * protects us from being used wrong)
       */
      if (ch[1] == 'Q')
      {
        para[0] = cli_name(cptr);
        from = cptr;
      }
      else
        return 0;
    }
    else if (cli_from(from) != cptr)
    {
      ++ServerStats->is_wrdi;
      Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
          buffer, cli_name(cptr)));
      return 0;
    }
  }
  else
  {
    char numeric_prefix[6];
    int  i;
    for (i = 0; i < 5; ++i)
    {
      if ('\0' == ch[i] || ' ' == (numeric_prefix[i] = ch[i]))
      {
        break;
      }
    }
    numeric_prefix[i] = '\0';

    /*
     * We got a numeric nick as prefix
     * 1 or 2 character prefixes are from servers
     * 3 or 5 chars are from clients
     */
    if (0 == i)
    {
      protocol_violation(cptr,"Missing Prefix");
      from = cptr;
    }
    else if (' ' == ch[1] || ' ' == ch[2])
      from = FindNServer(numeric_prefix);
    else
      from = findNUser(numeric_prefix);

    do
    {
      ++ch;
    }
    while (*ch != ' ' && *ch);

    /*
     * If the client corresponding to the
     * prefix is not found. We must ignore it,
     * it is simply a lagged message traveling
     * upstream a SQUIT that removed the client
     * --Run
     * There turned out to be other reasons that
     * a prefix is unknown, needing an upstream
     * KILL.  Also, next to an SQUIT we better
     * allow a KILL to pass too.
     * --Run
     */
    if (from == NULL)
    {
      ServerStats->is_unpf++;
      while (*ch == ' ')
        ch++;
      if (*ch == 'N' && (ch[1] == ' ' || ch[1] == 'I'))
        /* Only sent a KILL for a nick change */
      {
        struct Client *server;
        /* Kill the unknown numeric prefix upstream if
         * it's server still exists: */
        if ((server = FindNServer(numeric_prefix)) && cli_from(server) == cptr)
	  sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (Unknown numeric nick)",
			numeric_prefix, cli_name(&me));
      }
      /*
       * Things that must be allowed to travel
       * upstream against an squit:
       */
      if (ch[1] == 'Q' || (*ch == 'D' && ch[1] == ' ') ||
          (*ch == 'K' && ch[2] == 'L'))
        from = cptr;
      else
        return 0;
    }

    /* Let para[0] point to the name of the sender */
    para[0] = cli_name(from);

    if (cli_from(from) != cptr)
    {
      ServerStats->is_wrdi++;
      Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)",
          buffer, cli_name(cptr)));
      return 0;
    }
  }

  while (*ch == ' ')
    ch++;
  if (*ch == '\0')
  {
    ServerStats->is_empt++;
    Debug((DEBUG_NOTICE, "Empty message from host %s:%s",
        cli_name(cptr), cli_name(from)));
    return (-1);
  }

  /*
   * Extract the command code from the packet.   Point s to the end
   * of the command code and calculate the length using pointer
   * arithmetic.  Note: only need length for numerics and *all*
   * numerics must have parameters and thus a space after the command
   * code. -avalon
   */
  s = strchr(ch, ' ');          /* s -> End of the command code */
  len = (s) ? (s - ch) : 0;
  if (len == 3 && IsDigit(*ch))
  {
    numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0');
    paramcount = 2; /* destination, and the rest of it */
    ServerStats->is_num++;
    mptr = NULL;                /* Init. to avoid stupid compiler warning :/ */
  }
  else
  {
    if (s)
      *s++ = '\0';

    /* Version      Receive         Send
     * 2.9          Long            Long
     * 2.10.0       Tkn/Long        Long
     * 2.10.10      Tkn/Long        Tkn
     * 2.10.20      Tkn             Tkn
     *
     * Clients/unreg servers always receive/
     * send long commands   -record
     *
     * And for the record, this trie parser really does not care. - Dianora
     */

    mptr = msg_tree_parse(ch, &tok_tree);

    if (mptr == NULL)
    {
      mptr = msg_tree_parse(ch, &msg_tree);
    }

    if (mptr == NULL)
    {
      /*
       * Note: Give error message *only* to recognized
       * persons. It's a nightmare situation to have
       * two programs sending "Unknown command"'s or
       * equivalent to each other at full blast....
       * If it has got to person state, it at least
       * seems to be well behaving. Perhaps this message
       * should never be generated, though...   --msa
       * Hm, when is the buffer empty -- if a command
       * code has been found ?? -Armin
       */
#ifdef DEBUGMODE
      if (buffer[0] != '\0')
      {
        Debug((DEBUG_ERROR, "Unknown (%s) from %s",
              ch, get_client_name(cptr, HIDE_IP)));
      }
#endif
      ServerStats->is_unco++;
      return (-1);
    }

    paramcount = mptr->parameters;
    i = bufend - ((s) ? s : ch);
    mptr->bytes += i;
  }
  /*
   * Must the following loop really be so devious? On
   * surface it splits the message to parameters from
   * blank spaces. But, if paramcount has been reached,
   * the rest of the message goes into this last parameter
   * (about same effect as ":" has...) --msa
   */

  /* Note initially true: s==NULL || *(s-1) == '\0' !! */

  i = 0;
  if (s)
  {
    if (paramcount > MAXPARA)
      paramcount = MAXPARA;
    for (;;)
    {
      /*
       * Never "FRANCE " again!! ;-) Clean
       * out *all* blanks.. --msa
       */
      while (*s == ' ')
        *s++ = '\0';

      if (*s == '\0')
        break;
      if (*s == ':')
      {
        /*
         * The rest is single parameter--can
         * include blanks also.
         */
	if (numeric)
	  para[++i] = s; /* preserve the colon to make do_numeric happy */
	else
	  para[++i] = s + 1;
        break;
      }
      para[++i] = s;
      if (i >= paramcount)
        break;
      for (; *s != ' ' && *s; s++);
    }
  }
  para[++i] = NULL;
  if (numeric)
    return (do_numeric(numeric, (*buffer != ':'), cptr, from, i, para));
  mptr->count++;

  return (*mptr->handlers[cli_handler(cptr)]) (cptr, from, i, para);
}
예제 #19
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;
}
예제 #20
0
파일: m_trace.c 프로젝트: jast/ircu-now
static
void do_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[])
{
    int i;
    struct Client *acptr;
    struct Client *acptr2;
    const struct ConnectionClass* cl;
    char* tname;
    int doall;
    int *link_s;
    int *link_u;
    int cnt = 0;
    int wilds;
    int dow;

    if (parc < 2 || BadPtr(parv[1]))
    {
        /* just "TRACE" without parameters. Must be from local client */
        parc = 1;
        acptr = &me;
        tname = cli_name(&me);
        i = HUNTED_ISME;
    }
    else if (parc < 3 || BadPtr(parv[2]))
    {
        /* No target specified. Make one before propagating. */
        parc = 2;
        tname = parv[1];
        if ((acptr = find_match_server(parv[1])) ||
                ((acptr = FindClient(parv[1])) && !MyUser(acptr)))
        {
            if (IsUser(acptr))
                parv[2] = cli_name(cli_user(acptr)->server);
            else
                parv[2] = cli_name(acptr);
            parc = 3;
            parv[3] = 0;
            if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr),
                                     "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH)
                return;
        }
        else
            i = HUNTED_ISME;
    } else {
        /* Got "TRACE <tname> :<target>" */
        parc = 3;
        if (MyUser(sptr) || Protocol(cptr) < 10)
            acptr = find_match_server(parv[2]);
        else
            acptr = FindNServer(parv[2]);
        if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc,
                                 parv)) == HUNTED_NOSUCH)
            return;
        tname = parv[1];
    }

    if (i == HUNTED_PASS) {
        if (!acptr)
            acptr = next_client(GlobalClientList, tname);
        else
            acptr = cli_from(acptr);
        send_reply(sptr, RPL_TRACELINK,
                   version, debugmode, tname,
                   acptr ? cli_name(cli_from(acptr)) : "<No_match>");
        return;
    }

    doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1;
    wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?');
    dow = wilds || doall;

    /* Don't give (long) remote listings to lusers */
    if (dow && !MyConnect(sptr) && !IsAnOper(sptr)) {
        send_reply(sptr, RPL_TRACEEND);
        return;
    }

    link_s = MyCalloc(2 * maxconnections, sizeof(link_s[0]));
    link_u = link_s + maxconnections;

    if (doall) {
        for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
            if (IsUser(acptr))
                link_u[cli_fd(cli_from(acptr))]++;
            else if (IsServer(acptr))
                link_s[cli_fd(cli_from(acptr))]++;
        }
    }

    /* report all direct connections */

    for (i = 0; i <= HighestFd; i++) {
        const char *conClass;

        if (!(acptr = LocalClientArray[i])) /* Local Connection? */
            continue;
        if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) &&
                !IsAnOper(acptr) && (acptr != sptr))
            continue;
        if (!doall && wilds && match(tname, cli_name(acptr)))
            continue;
        if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr)))
            continue;

        conClass = get_client_class(acptr);

        switch (cli_status(acptr)) {
        case STAT_CONNECTING:
            send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr));
            cnt++;
            break;
        case STAT_HANDSHAKE:
            send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr));
            cnt++;
            break;
        case STAT_ME:
            break;
        case STAT_UNKNOWN:
        case STAT_UNKNOWN_USER:
            send_reply(sptr, RPL_TRACEUNKNOWN, conClass,
                       get_client_name(acptr, HIDE_IP));
            cnt++;
            break;
        case STAT_UNKNOWN_SERVER:
            send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server");
            cnt++;
            break;
        case STAT_USER:
            /* Only opers see users if there is a wildcard
               but anyone can see all the opers. */
            if ((IsAnOper(sptr) && (MyUser(sptr) ||
                                    !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) {
                if (IsAnOper(acptr))
                    send_reply(sptr, RPL_TRACEOPERATOR, conClass,
                               get_client_name(acptr, SHOW_IP),
                               CurrentTime - cli_lasttime(acptr));
                else
                    send_reply(sptr, RPL_TRACEUSER, conClass,
                               get_client_name(acptr, SHOW_IP),
                               CurrentTime - cli_lasttime(acptr));
                cnt++;
            }
            break;
        /*
         * Connection is a server
         *
         * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age>
         *
         * class        Class the server is in
         * nS           Number of servers reached via this link
         * nC           Number of clients reached via this link
         * name         Name of the server linked
         * ConnBy       Who established this link
         * last         Seconds since we got something from this link
         * age          Seconds this link has been alive
         *
         * Additional comments etc......        -Cym-<*****@*****.**>
         */

        case STAT_SERVER:
            if (cli_serv(acptr)->user) {
                if (!cli_serv(acptr)->by[0]
                        || !(acptr2 = findNUser(cli_serv(acptr)->by))
                        || (cli_user(acptr2) != cli_serv(acptr)->user))
                    acptr2 = NULL;
                send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
                           link_u[i], cli_name(acptr),
                           acptr2 ? cli_name(acptr2) : "*",
                           cli_serv(acptr)->user->username,
                           cli_serv(acptr)->user->host,
                           CurrentTime - cli_lasttime(acptr),
                           CurrentTime - cli_serv(acptr)->timestamp);
            } else
                send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i],
                           link_u[i], cli_name(acptr),
                           (*(cli_serv(acptr))->by) ?  cli_serv(acptr)->by : "*", "*",
                           cli_name(&me), CurrentTime - cli_lasttime(acptr),
                           CurrentTime - cli_serv(acptr)->timestamp);
            cnt++;
            break;
        default:                  /* We actually shouldn't come here, -msa */
            send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP));
            cnt++;
            break;
        }
    }
    /*
     * Add these lines to summarize the above which can get rather long
     * and messy when done remotely - Avalon
     */
    if (IsAnOper(sptr) && doall) {
        for (cl = get_class_list(); cl; cl = cl->next) {
            if (Links(cl) > 1)
                send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl) - 1);
        }
    }
    send_reply(sptr, RPL_TRACEEND);
    MyFree(link_s);
}