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

m_nick.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: m_nick.c,v 1.87.2.3 2005/01/15 23:53:32 amcwilliam Exp $
 */

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "h.h"
#include "memory.h"
#include "hook.h"
#include "modules.h"
#include "xmode.h"
#include "user_ban.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

Hook *h_nick_change_local = NULL;
Hook *h_nick_change_remote = NULL;

Module MOD_HEADER(m_nick) = {
      "m_nick",
      "/NICK command",
      6, "$Revision: 1.87.2.3 $"
};

int MOD_LOAD(m_nick)()
{
      if ((h_nick_change_local = register_hook(&MOD_HEADER(m_nick), "h_nick_change_local")) == NULL) {
            return MOD_FAILURE;
      }
      if ((h_nick_change_remote = register_hook(&MOD_HEADER(m_nick), "h_nick_change_remote")) == NULL) {
            return MOD_FAILURE;
      }
      
      if (register_command(&MOD_HEADER(m_nick), &CMD_NICK, m_nick) == NULL) {
            return MOD_FAILURE;
      }

      MOD_SET_FLAG(&MOD_HEADER(m_nick), MOD_FLAG_PERM);
      return MOD_SUCCESS;
}

int MOD_UNLOAD(m_nick)()
{
      return MOD_SUCCESS;
}

static int valid_nick_name(char *nick)
{
      if (BadPtr(nick) || (*nick == '-') || IsDigit(*nick)) {
            return 0;
      }

      while (*nick != '\0') {
            if (!IsNickChar(*nick)) {
                  return 0;
            }
            nick++;
      }

      return 1;
}

/*
 * m_nick
 *    parv[0] = sender prefix
 *    parv[1] = nickname
 *    parv[2] = hopcount
 *    parv[3] = TS or !TS
 *    parv[4] = usermodes
 *    parv[5] = username
 *    parv[6] = hostname
 *    parv[7] = server or !suid
 *    parv[8] = servicestamp or !servicestamp
 *    parv[9] = IP or !IP
 *    parv[10] = realname
 */
static int handle_nick_server(int remote_nick_change, aClient *cptr, aClient *sptr, int parc, char *parv[])
{
      aClient *acptr, *uplink;
      char nick[NICKLEN + 1], *p;
      int sameuser = 0, samenick = 0, i;
      time_t newts = 0;

      newts = (remote_nick_change) ? strtoul(parv[2], NULL, 10) : get_ts(parv[3]);

      strncpyzt(nick, parv[1], NICKLEN + 1);

      if (!valid_nick_name(nick)) {
            send_me_numeric(sptr, ERR_ERRONEOUSNICKNAME, nick, "N/A");
            ircstp->is_kill++;

            sendto_realops_lev(DEBUG_LEV, "Bad nickname: %s From: %s %s", parv[1],
                  parv[0], get_client_name(cptr, FALSE));
            sendto_one_client_real(cptr, NULL, &me, &CMD_KILL, "%s :Bad nickname", parv[1]);

            if (sptr != cptr) {
                  sendto_serv_msg_butone(cptr, &me, &CMD_KILL, "%s :Bad nickname", parv[0]);
                  SetKilled(sptr);
                  return exit_client(cptr, sptr, &me, "Bad nickname");
            }

            return 0;
      }

      do {
            if ((acptr = find_client(nick, NULL)) == NULL) {
                  break;
            }
            if (acptr == sptr) {
                  if (irccmp(acptr->name, nick)) {
                        break;
                  }
                  else {
                        return 0;
                  }
            }
            if (IsUnknown(acptr)) {
                  exit_client(NULL, acptr, &me, "Overridden by older remote signon");
                  break;
            }
            if (!remote_nick_change) {
                  if (!newts || !acptr->tsinfo || newts == acptr->tsinfo) {
                        sendto_realops_lev(SKILL_LEV, "Nick collision on %s", parv[1]);
                        ircstp->is_kill++;
                        send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
                        sendto_serv_kill_msg_butone(NULL, &me, acptr, ":%s (Nick collision)",
                              me.name);
                        SetKilled(acptr);
                        return exit_client(cptr, acptr, &me, "Nick collision");
                  }
                  else {
                        sameuser = ((acptr->user != NULL) && !mycmp(acptr->username, parv[5])
                          && !mycmp(acptr->host, parv[6]));
                        if ((sameuser && newts < acptr->tsinfo) || (!sameuser &&
                          newts > acptr->tsinfo)) {
                              return 0;
                        }
                        else {
                              sendto_realops_lev(SKILL_LEV, "Nick collision on %s", parv[1]);
                              ircstp->is_kill++;
                              send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
                              sendto_serv_kill_msg_butone(NULL, &me, acptr, ":%s (Nick collision)",
                                    me.name);
                              SetKilled(acptr);
                              exit_client(cptr, acptr, &me, "Nick collision");
                              break;
                        }
                  }     
            }
            else {
                  if (!newts || !acptr->tsinfo || (newts == acptr->tsinfo) ||
                    sptr->user == NULL) {
                        sendto_realops_lev(SKILL_LEV, "Nick change collision: %s", parv[1]);

                        ircstp->is_kill++;
                        send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
                        sendto_serv_kill_msg_butone(NULL, &me, sptr, ":%s (Nick collision)", me.name);

                        ircstp->is_kill++;
                        send_me_numeric(sptr, ERR_NICKCOLLISION, sptr->name);
                        sendto_serv_kill_msg_butone(NULL, &me, acptr, ":%s (Nick collision)", me.name);

                        SetKilled(acptr);
                        exit_client(NULL, acptr, &me, "Nick change collision");

                        SetKilled(sptr);
                        return exit_client(cptr, sptr, &me, "Nick change collision");
                  }
                  else {
                        sameuser = (!mycmp(acptr->username, sptr->username) &&
                          !mycmp(acptr->host, sptr->host));
                        if ((sameuser && newts < acptr->tsinfo) || (!sameuser &&
                          newts > acptr->tsinfo)) {
                              sendto_realops_lev(SKILL_LEV, "Nick change collision "
                                    "from %s to %s", sptr->name, acptr->name);
                              ircstp->is_kill++;
                              sendto_one_client_real(cptr, sptr, &me, &CMD_KILL, ":%s "
                                    "(Nick collision)", me.name);
                              SetKilled(sptr);
                              return exit_client(cptr, sptr, &me,
                                    sameuser ? "Nick collision (old)" : "Nick collision (new)");
                        }
                        else {
                              sendto_realops_lev(SKILL_LEV, "Nick collision on %s", acptr->name);
                              ircstp->is_kill++;
                              send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);
                              sendto_serv_kill_msg_butone(cptr, &me, acptr, ":%s (Nick collision)",
                                    me.name);
                              SetKilled(acptr);
                              exit_client(cptr, acptr, &me, "Nick collision");
                        }
                  }
            }
      } while (0);

      if (!remote_nick_change) {
            unsigned long newid;
            char *b64id = NULL;

            if (is_id(parv[7])) {
                  aClient *bucptr;

                  b64id = parv[7];

                  if ((bucptr = find_by_base64_id(b64id)) != NULL) {
                        sendto_realops_lev(DEBUG_LEV, "IDENTITY COLLISION! (%s[%s][%s] <> "
                              "%s[%s][%s])", sptr->name, sptr->uplink->name, b64id,
                              bucptr->name, bucptr->uplink->name, bucptr->id.string);
                        exit_client(cptr, bucptr, &me, "Identity collision");
                  }

                  if ((uplink = find_serv_by_base64_id(b64id, &newid)) != NULL) {
                        parv[7] = uplink->name;
                  }
            }
            else {
                  uplink = find_server(parv[7], NULL);
            }
            if (uplink == NULL) {
                  sendto_realops("Remote nick %s on UNKNOWN server %s", nick, parv[7]);
                  return 0;
            }

            sptr = make_client(cptr, uplink);

            if (IsULine(uplink)) {
                  SetULine(sptr);
            }

            add_client_to_list(sptr);
            sptr->hopcount = atoi(parv[2]);

            if (newts > 0) {
                  sptr->tsinfo = newts;
            }
            else {
                  newts = sptr->tsinfo = timeofday;
                  ircdlog(LOG_ERROR, "Remote nick %s introduced with no TS", nick);
            }

            if (b64id != NULL) {
                  SetSUID(sptr);
                  sptr->id.id = newid;
                  strncpyzt(sptr->id.string, b64id, IDLEN + 1);
                  add_userid_to_serv(uplink, sptr);
            }

            strcpy(sptr->name, nick);
            add_to_client_hash_table(nick, sptr);

            for (p = (parv[4] + 1); *p != '\0'; p++) {
                  if ((i = usermodes->map[(unsigned char)*p]) != -1) {
                        if (usermodes->table[i].mode == UMODE_OPER) {
                              Count.oper++;
                        }
                        if (usermodes->table[i].mode == UMODE_INVISIBLE) {
                              Count.invisi++;
                        }
                        sptr->umode |= (usermodes->table[i].mode & SEND_UMODES);
                        break;
                  }
            }

            return do_user(nick, cptr, sptr, parv[5], parv[6], parv[7], get_ts(parv[8]),
                  (parc == 11) ? get_ts(parv[9]) : 0, parv[parc - 1]);
      }
      else {
            HookData hdata = HOOKDATA_INIT;

            if (mycmp(sptr->name, nick)) {
                  sptr->tsinfo = (newts > 0) ? newts : timeofday;
                  DelMode(sptr, UMODE_REGNICK);
            }

            sendto_channel_local_msg_common(sptr, &CMD_NICK, ":%s", nick);
            add_history(sptr, 1);
            sendto_serv_msg_butone(cptr, sptr, &CMD_NICK, "%s %ld", nick, sptr->tsinfo);

            del_from_client_hash_table(sptr->name, sptr);
            samenick = mycmp(sptr->name, nick) ? 0 : 1;
            if (!samenick) {
                  hash_check_watch(sptr, RPL_LOGOFF);
            }

            strcpy(sptr->name, nick);
            add_to_client_hash_table(nick, sptr);

            if (!samenick) {
                  hash_check_watch(sptr, RPL_LOGON);
            }

            hdata.cptr = cptr;
            hdata.sptr = sptr;
            hook_run(h_nick_change_remote, &hdata);
      }
      return 0;
}

/*
 * m_nick
 *    if (parc == 2): new local user, local user nickchange (from user)
 *          parv[0] = sender prefix
 *          parv[1] = nickname
 *    if (parc == 3): remote user nickchange (from server)
 *          parv[2] = TS
 *    if (parc > 3): new remote user (from server)
 */
int m_nick(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
      char nick[NICKLEN + 1];
      aClient *acptr;
      simBan *sban;
      int samenick;

      if (parc < 2 || BadPtr(parv[1])) {
            if (IsPerson(sptr)) {
                  send_me_numericNA(sptr, ERR_NONICKNAMEGIVEN);
            }
            return 0;
      }
      if (parc > 3 && IsServer(sptr)) {
            if (parc < 10) {
                  ircdlog(LOG_ERROR, "IGNORING BAD NICK: %s[%s@%s] on %s (from %s)",
                        parv[1], (parc >= 6) ? parv[5] : "-",
                        (parc >= 7) ? parv[6] : "-", (parc >= 8) ? parv[7] : "-",
                        parv[0]);
                  sendto_realops_lev(DEBUG_LEV, "IGNORING BAD NICK: %s[%s@%s] on %s (from %s)",
                        parv[1], (parc >= 6) ? parv[5] : "-",
                        (parc >= 7) ? parv[6] : "-", (parc >= 8) ? parv[7] : "-",
                        parv[0]);
                  return 0;
            }
            return handle_nick_server(0, cptr, sptr, parc, parv);
      }
      else if (parc > 2 && !IsServer(sptr) && IsServer(cptr)) {
            return handle_nick_server(1, cptr, sptr, parc, parv);
      }

      strncpyzt(nick, parv[1], NICKLEN + 1);

      if (!valid_nick_name(nick)) {
            send_me_numeric(sptr, ERR_ERRONEOUSNICKNAME, nick, "N/A");
            return 0;
      }

      do {
            if ((acptr = find_client(nick, NULL)) == NULL) {
                  break;
            }
            if (acptr == sptr) {
                  if (irccmp(acptr->name, nick)) {
                        break;
                  }
                  return 0;
            }
            if (IsUnknown(acptr)) {
                  exit_client(NULL, acptr, &me, "Overridden by older local signon");
                  break;
            }

            send_me_numeric(sptr, ERR_NICKNAMEINUSE, nick);
            return 0;
      } while (0);

      if (*sptr->name != '\0') {
            HookData hdata = HOOKDATA_INIT;

            if (!HasMode(sptr, UMODE_OPER) && !IsULine(sptr)) {
                  if ((sban = find_simban_flags(nick, SBAN_NICK)) != NULL) {
                        send_me_numeric(sptr, ERR_ERRONEOUSNICKNAME, nick, BanReason(sban));
                        sendto_realops_lev(REJ_LEV, "Forbidding restricted nickname %s from %s.",
                              nick, get_client_name(sptr, FALSE));
                        return 0;
                  }
            }

            if (mycmp(sptr->name, nick)) {
                  sptr->tsinfo = timeofday;
                  DelMode(sptr, UMODE_REGNICK);
            }
            if (IsPerson(sptr)) {
                  chanMember *cm;
                  for (cm = sptr->user->channel; cm != NULL; cm = cm->nextchan) {
                        if (can_send(sptr, cm->chptr, NULL)) {
                              send_me_numeric(sptr, ERR_BANNICKCHANGE, cm->chptr->chname);
                              return 0;
                        }
                        if ((cm->chptr->mode.mode & CMODE_NONICKCH) && !IsULine(sptr)
                          && (!HasMode(sptr, UMODE_SADMIN) || GeneralConfig.restrict_chan_override)
                          && !is_chanadmin(sptr, cm->chptr)) {
                              send_me_notice(sptr, ":Channel %s does not allow nickname "
                                    "changes", cm->chptr->chname);
                              return 0;
                        }
                  }
                  if (FloodConfig.anti_nick_flood
                    && (!HasMode(sptr, UMODE_SADMIN) || GeneralConfig.restrict_chan_override)) {
                        if ((sptr->localUser->last_nick_change + ANTI_NICK_FLOOD_TIME) < timeofday) {
                              sptr->localUser->nick_changes = 0;
                        }

                        sptr->localUser->last_nick_change = timeofday;
                        sptr->localUser->nick_changes++;

                        if (sptr->localUser->nick_changes > ANTI_NICK_FLOOD_CHANGES) {
                              send_me_notice(sptr, ":Too many nickname changes! Please "
                                    "wait %d seconds before trying again.", ANTI_NICK_FLOOD_TIME);
                              return 0;
                        }
                  }

                  sendto_channel_local_msg_common(sptr, &CMD_NICK, ":%s", nick);
                  if (sptr->user != NULL) {
                        add_history(sptr, 1);
                        sendto_serv_msg_butone(sptr, sptr, &CMD_NICK, "%s %ld",
                              nick, sptr->tsinfo);
                  }
            }

            del_from_client_hash_table(sptr->name, sptr);
            samenick = mycmp(sptr->name, nick) ? 0 : 1;
            if (IsPerson(sptr) && !samenick) {
                  hash_check_watch(sptr, RPL_LOGOFF);
            }

            strcpy(sptr->name, nick);
            add_to_client_hash_table(nick, sptr);

            if (IsPerson(sptr) && !samenick) {
                  hash_check_watch(sptr, RPL_LOGON);
            }

            hdata.sptr = sptr;
            hook_run(h_nick_change_local, &hdata);
      }
      else {
            if ((sban = find_simban_flags(nick, SBAN_NICK)) != NULL) {
                  send_me_numeric(sptr, ERR_ERRONEOUSNICKNAME, nick, BanReason(sban));
                  sendto_realops_lev(REJ_LEV, "Forbidding restricted nickname %s from (unregistered) %s.",
                        nick, get_client_name(sptr, FALSE));
                  return 0;
            }

            strcpy(sptr->name, nick);
            sptr->tsinfo = timeofday;
            add_to_client_hash_table(nick, sptr);

            if (sptr->user != NULL) {
                  if (register_user(cptr, sptr, nick, sptr->username) == FLUSH_BUFFER) {
                        return FLUSH_BUFFER;
                  }
            }
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index