/************************************************************************
 *   IRC - Internet Relay Chat, user/register.c
 *   This file is copyright (C) 2001 Andrew Suffield
 *                                    <asuffield@users.sourceforge.net>
 *   Copyright (C) 1990 Jarkko Oikarinen and
 *                      University of Oulu, Computing Center
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include "config.h"
#include "class.h"
#include "ccr.h"
#include "channel.h"
#include "client.h"
#include "database/database.h"
#include "handlers.h"
#include "hash.h"
#include "irc_string.h"
#include "ircd.h"
#include "listener.h"
#include "msg.h"
#include "numeric.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_cr.h"
#include "s_log.h"
#include "s_stats.h"
#include "s_user.h"
#include "send.h"
#include "struct.h"
#include "umodes.h"
#include "user.h"
#include "whowas.h"
#include "support/verify.h"

#include <stdlib.h>
#include <assert.h>
#include <ctype.h>

static time_t record_time = 0;

/* 
 * tell_user_off
 *
 * inputs       - client pointer of user to tell off
 *              - pointer to reason user is getting told off
 * output       - drop connection now YES or NO (for reject hold)
 * side effects -
 */

static int
tell_user_off(aClient * cptr, char **preason)
{
#ifdef KLINE_WITH_REASON
  char *p = 0;
#endif /* KLINE_WITH_REASON */

  /* Ok... if using REJECT_HOLD, I'm not going to dump
   * the client immediately, but just mark the client for exit
   * at some future time, .. this marking also disables reads/
   * writes from the client. i.e. the client is "hanging" onto
   * an fd without actually being able to do anything with it
   * I still send the usual messages about the k line, but its
   * not exited immediately.
   * - Dianora
   */

#ifdef REJECT_HOLD
  if ((reject_held_fds != REJECT_HELD_MAX))
    {
      SetRejectHold(cptr);
      reject_held_fds++;
#endif

#ifdef KLINE_WITH_REASON
      if (*preason)
	{
	  if ((p = strchr(*preason, '|')))
	    *p = '\0';

	  sendto_one(cptr, ":%s NOTICE %s :*** Banned: %s",
		     me.name, cptr->name, *preason);

	  if (p)
	    *p = '|';
	}
      else
#endif
	sendto_one(cptr, get_str(STR_YOUREBANNED),
		   me.name, cptr->name);
#ifdef REJECT_HOLD
      return 0;
    }
#endif

  return 1;
}

static int
check_ccrs(aClient * cptr, const char *username, char **preason)
{
  char non_ident[USERLEN + 1];
  struct client_connect_record *allow_ccr, *deny_ccr;

  allow_ccr = find_prevalent_allow_ccr(cptr->host);
  deny_ccr = find_prevalent_deny_ccr(cptr->host);

  if (!allow_ccr)
    return -1; /* No match */

  if (deny_ccr)
    /* Deny beats allow at equal priority */
    if (deny_ccr->priority >= allow_ccr->priority)
      {
	*preason = deny_ccr->reason;
	return -5;
      }

  if (!BadPtr(allow_ccr->passwd)
      && 0 != strcmp(libshadow_md5_crypt(cptr->passwd, allow_ccr->passwd),
		     allow_ccr->passwd))
    return -6;
  /* Clear it for security */
  memset(cptr->passwd, 0, sizeof(cptr->passwd));

  cptr->c_class = find_class(allow_ccr->class);

  /* OK, looks like they get to connect */
  cptr->ccr_priority = allow_ccr->priority;
  if (allow_ccr->flags & CCONNECT_FLAGS_SPOOF)
    {
#ifdef SPOOF_NOTICE
      sendto_ops_flag(UMODE_CCONN, "%s spoofing: %s(%s) as %s",
		      cptr->name, cptr->host,
		      inetntoa((char *) &cptr->ip), allow_ccr->spoof_host);
#endif /* SPOOF_NOTICE */
      strncpy_irc(cptr->host, allow_ccr->spoof_host, HOSTLEN);
      SetIPSpoof(cptr);
      SetIPHidden(cptr);
    }
  if (allow_ccr->flags & CCONNECT_FLAGS_NO_TILDE)
    SetNoTilde(cptr);
  if (allow_ccr->flags & CCONNECT_FLAGS_ALLOW_BOTS)
    SetBlined(cptr);
  if (allow_ccr->flags & CCONNECT_FLAGS_NEED_IDENT)
    SetReqIdent(cptr);

  return 0;
}

/*
 * Ordinary client access check. Look for conf lines which have the same
 * status as the flags passed.
 *
 * outputs
 *  0 = Success
 * -1 = Access denied (no I line match)
 * -2 = Bad socket.
 * -3 = I-line is full
 * -4 = Too many connections from hostname
 * -5 = K-lined
 * also updates reason if a K-line
 *
 */
int
check_client(struct Client *cptr, char *username, char **reason)
{
  static char sockname[HOSTLEN + 1];
  int i;

  ClearAccess(cptr);

  if ((i = check_ccrs(cptr, username, reason)))
    {
      log(L_INFO, "Access denied: %s[%s]", cptr->name, sockname);
      return i;
    }

  return 0;
}

/*
 * register_user
 *      This function is called when both NICK and USER messages
 *      have been accepted for the client, in whatever order. Only
 *      after this, is the USER message propagated.
 *
 *      NICK's must be propagated at once when received, although
 *      it would be better to delay them too until full info is
 *      available. Doing it is not so simple though, would have
 *      to implement the following:
 *
 *      (actually it has been implemented already for a while) -orabidoo
 *
 *      1) user telnets in and gives only "NICK foobar" and waits
 *      2) another user far away logs in normally with the nick
 *         "foobar" (quite legal, as this server didn't propagate
 *         it).
 *      3) now this server gets nick "foobar" from outside, but
 *         has already the same defined locally. Current server
 *         would just issue "KILL foobar" to clean out dups. But,
 *         this is not fair. It should actually request another
 *         nick from local user or kill him/her...
 */

int
register_user(struct Client *cptr, struct Client *sptr, char *nick, char *username)
{
  aConfItem *aconf;
  char *parv[3];
  static char ubuf[BUFSIZE];
  anUser *user = sptr->user;
  char *reason;
  char tmpstr2[512];

  assert(0 != sptr);
  assert(sptr->username != username);

  user->last = CurrentTime;
  parv[0] = sptr->name;
  parv[1] = parv[2] = NULL;

  /* pointed out by Mortiis, never be too careful */
  if (strlen(username) > USERLEN)
    username[USERLEN] = '\0';

  reason = NULL;

#define NOT_AUTHORIZED  (-1)
#define SOCKET_ERROR    (-2)
#define I_LINE_FULL     (-3)
#define I_LINE_FULL2    (-4)
#define BANNED_CLIENT   (-5)
#define WRONG_PASSWORD  (-6)

  if (MyConnect(sptr))
    {
      if (!strcmp(sptr->host, "localhost"))
	strcpy(sptr->host, me.name);
      switch (check_client(sptr, username, &reason))
	{
	case SOCKET_ERROR:
	  return exit_client(cptr, sptr, &me, "Socket Error");
	  break;

	case I_LINE_FULL:
	case I_LINE_FULL2:
	  sendto_ops_flag(UMODE_FULL, "%s for %s (%s).",
			  "I-line is full",
			  get_client_host(sptr),
			  inetntoa((char *) &sptr->ip));
	  log(L_INFO, "Too many connections from %s.", get_client_host(sptr));
	  ServerStats->is_ref++;
	  return exit_client(cptr, sptr, &me,
			     "No more connections allowed in your connection class");
	  break;

	case NOT_AUTHORIZED:

#ifdef REJECT_HOLD

	  /* Slow down the reconnectors who are rejected */
	  if ((reject_held_fds != REJECT_HELD_MAX))
	    {
	      SetRejectHold(cptr);
	      reject_held_fds++;
	      release_client_dns_reply(cptr);
	      return 0;
	    }
	  else
#endif
	    {
	      ServerStats->is_ref++;
	      /* jdc - lists server name & port connections are on */
	      /*       a purely cosmetical change */
	      sendto_ops_flag(UMODE_CCONN,
			      "%s from %s [%s] on [%s/%u].",
			      "Unauthorized client connection",
			      get_client_host(sptr),
			      inetntoa((char *) &sptr->ip),
			      sptr->listener->name, sptr->listener->port);
	      log(L_INFO,
		  "Unauthorized client connection from %s on [%s/%u].",
		  get_client_host(sptr),
		  sptr->listener->name, sptr->listener->port);

	      ServerStats->is_ref++;
	      return exit_client(cptr, sptr, &me,
				 "You are not authorized to use this server");
	    }
	  break;

	case BANNED_CLIENT:
	  {
	    if (!IsGotId(sptr))
	      {
		*sptr->username = '~';
		strncpy_irc(&sptr->username[1], username, USERLEN - 1);
		sptr->username[USERLEN] = '\0';
	      }

	    if (tell_user_off(sptr, &reason))
	      {
		ServerStats->is_ref++;
		return exit_client(cptr, sptr, &me, "Banned");
	      }
	    else
	      return 0;

	    break;
	  }
	case WRONG_PASSWORD:
	  ServerStats->is_ref++;
	  sendto_one(sptr, form_str(ERR_PASSWDMISMATCH), me.name, parv[0]);
	  return exit_client(cptr, sptr, &me, "Bad Password");

	default:
	  release_client_dns_reply(cptr);
	  break;
	}

      if (!valid_hostname(sptr->host))
	{
	  sendto_one(sptr,
		     ":%s NOTICE %s :*** Notice -- hostname %s isn't valid, using default",
		     me.name, sptr->name, sptr->host);

	  strncpy(sptr->host, sptr->sockhost, HOSTIPLEN + 1);
	}

      if (!IsGotId(sptr))
	{
	  if (IsReqIdent(sptr))
	    {
	      ServerStats->is_ref++;
	      sendto_one(sptr,
			 ":%s NOTICE %s :*** Notice -- You need to install identd to use this server",
			 me.name, cptr->name);
	      return exit_client(cptr, sptr, &me, "Install identd");
	    }
	  if (IsNoTilde(sptr))
	    {
	      strncpy_irc(sptr->username, username, USERLEN);
	    }
	  else
	    {
	      *sptr->username = '~';
	      strncpy_irc(&sptr->username[1], username, USERLEN - 1);
	    }
	  sptr->username[USERLEN] = '\0';
	}

      /* Limit clients */
      /*
       * We want to be able to have servers and F-line clients
       * connect, so save room for "buffer" connections.
       * Smaller servers may want to decrease this, and it should
       * probably be just a percentage of the MAXCLIENTS...
       *   -Taner
       */
      /* Except "F:" clients */
      if ((
#ifdef BOTCHECK
	    !sptr->isbot &&
#endif /* BOTCHECK */
	    ((Count.local + 1) >= (MAXCLIENTS + MAX_BUFFER))) ||
	  (((Count.local + 1) >= (MAXCLIENTS - 5)) && !(IsFlined(sptr))))
	{
	  sendto_ops_flag(UMODE_FULL,
			  "Too many clients, rejecting %s[%s].",
			  nick, sptr->host);
	  ServerStats->is_ref++;
	  return exit_client(cptr, sptr, &me,
			     "Sorry, server is full - try later");
	}
      /* botcheck */
#ifdef BOTCHECK
      if (sptr->isbot)
	{
	  if (IsBlined(sptr))
	    {
	      sendto_ops_flag(UMODE_BOTS,
			      "Possible %s: %s (%s@%s) [B-lined]",
			      type_of_bot[sptr->isbot],
			      sptr->name, sptr->username, sptr->host);
	    }
	  else
	    {
	      sendto_ops_flag(UMODE_BOTS, "Rejecting %s: %s",
			      type_of_bot[sptr->isbot],
			      get_client_name(sptr, SHOW_IP));
	      ServerStats->is_ref++;
	      return exit_client(cptr, sptr, sptr, type_of_bot[sptr->isbot]);
	    }
	}
#endif
      /* End of botcheck */

      /* valid user name check */

      if (!valid_username(sptr->username))
	{
	  sendto_ops_flag(UMODE_REJ, "Invalid username: %s (%s@%s)",
			  nick, sptr->username, sptr->host);
	  ServerStats->is_ref++;
	  ircsprintf(tmpstr2, "Invalid username [%s]", sptr->username);
	  return exit_client(cptr, sptr, &me, tmpstr2);
	}
      /* end of valid user name check */

      if (!CannotBeXlined(sptr))
	{
	  char *xreason;

	  if ((aconf = find_special_conf(sptr->info, CONF_XLINE)))
	    {
	      if (aconf->passwd)
		xreason = aconf->passwd;
	      else
		xreason = "NONE";

	      if (aconf->port)
		{
		  if (aconf->port == 1)
		    {
		      sendto_ops_flag(UMODE_REJ,
				      "X-line Rejecting [%s] [%s], user %s",
				      sptr->info,
				      xreason, get_client_name(cptr, SHOW_IP));
		    }
		  ServerStats->is_ref++;
		  return exit_client(cptr, sptr, &me, "Bad user info");
		}
	      else
		sendto_ops_flag(UMODE_REJ,
				"X-line Warning [%s] [%s], user %s",
				sptr->info,
				xreason, get_client_name(cptr, SHOW_IP));
	    }
	}

      if ((++Count.local) > Count.max_loc)
	{
	  Count.max_loc = Count.local;
	  if (!(Count.max_loc % 10))
	    sendto_ops_flag(UMODE_SERVNOTICE, "New local record: %d users",
			    Count.max_loc);
	}
    }
  else
    {
      struct Client *acptr = find_server(user->server);

      strncpy_irc(sptr->username, username, USERLEN);
      if (acptr)
	{
	  /* OK, it's from a remote server. I'm going to check against *MY* ccrs. */
	  struct client_connect_record *allow_ccr = find_prevalent_allow_ccr(sptr->host);
	  struct client_connect_record *deny_ccr = find_prevalent_deny_ccr(sptr->host);

	  /* This needs rewriting... */

#if 0
	  if (deny_ccr && (!allow_ccr || (deny_ccr->priority >= allow_ccr->priority)))
	    {
	      /* Oh no you don't. No dodging */
	      sendto_ops_flag(UMODE_SKILL,
			      "Remote user %s K:lined here (%s), propagating K:line to %s",
			      sptr->name, me.name, sptr->user->server);
	      if (deny_ccr->until)
		sendto_one(acptr, ":%s KLINE %s until %.1lu priority %.1u :%s",
			   sptr->name, deny_ccr->host, deny_ccr->until, deny_ccr->priority,
			   deny_ccr->reason ? deny_ccr->reason : "");
	      else
		sendto_one(acptr, ":%s KLINE %s priority %.1u :%s",
			   sptr->name, deny_ccr->host, deny_ccr->priority,
			   deny_ccr->reason ? deny_ccr->reason : "");
	    }
#endif
	}
    }

  SetClient(sptr);

  sptr->servptr = find_server(user->server);
  if (!sptr->servptr)
    {
      sendto_ops_flag(UMODE_SERVNOTICE,
		      "Ghost killed: %s on invalid server %s", sptr->name,
		      sptr->user->server);
      sendto_one(cptr, ":%s KILL %s :%s (Ghosted, %s doesn't exist)",
		 me.name, sptr->name, me.name, user->server);
      sptr->flags |= FLAGS_KILLED;
      return exit_client(NULL, sptr, &me, "Ghost");
    }
  add_client_to_llist(&(sptr->servptr->serv->users), sptr);

  /* Move this down here and only send it locally, to cut down on network noise
   *  -- asuffield 
   */
  sendto_local_ops_flag(UMODE_CCONN,
			"Client connecting: %s (%s@%s) [%s] [%s]",
			nick, sptr->username, sptr->host,
			inetntoa((char *) &sptr->ip), sptr->servptr->name);

/* Increment our total user count here */
  if (++Count.total > Count.max_tot)
    {
      Count.max_tot = Count.total;
      if ((!(Count.max_tot % 2)) && (record_time < CurrentTime - 30))
	{
	  sendto_local_ops_flag(UMODE_SERVNOTICE,
				"New network record: %d users (%d local)",
				Count.max_tot, Count.max_loc);
	  record_time = CurrentTime;
	}
    }
  if (MyConnect(sptr))
    {
      sendto_one(sptr, form_str(RPL_WELCOME), me.name, nick, nick);
      /* This is a duplicate of the NOTICE but see below... */
      sendto_one(sptr, form_str(RPL_YOURHOST), me.name, nick,
		 get_listener_name(sptr->listener), version);

      /*
          * Don't mess with this one - IRCII needs it! -Avalon
       */
      sendto_one(sptr,
		 "NOTICE %s :*** Your host is %s, running version %s",
		 nick, get_listener_name(sptr->listener), version);

      sendto_one(sptr, form_str(RPL_CREATED), me.name, nick, creation);
      sendto_one(sptr, form_str(RPL_MYINFO), me.name, parv[0],
		 me.name, version, umode_list);
      /* Increment the total number of clients since (re)start */
      Count.totalrestartcount++;
      show_lusers(sptr, sptr, 1, parv);

#ifdef SHORT_MOTD
      sendto_one(sptr,
		 "NOTICE %s :*** Notice -- MOTD was last changed at %s",
		 sptr->name, ConfigFileEntry.motd.lastChangedDate);

      sendto_one(sptr,
		 "NOTICE %s :*** Notice -- If you haven't read the MOTD, please do (/motd command)",
		 sptr->name);

      sendto_one(sptr, form_str(RPL_MOTDSTART), me.name, sptr->name, me.name);

      sendto_one(sptr,
		 form_str(RPL_MOTD),
		 me.name, sptr->name, "*** This is the short motd ***");

      sendto_one(sptr, form_str(RPL_ENDOFMOTD), me.name, sptr->name);
#else
      SendMessageFile(sptr, &ConfigFileEntry.motd);
#endif

#ifdef NEED_SPLITCODE
      if (server_was_split)
	{
	  sendto_one(sptr,
		     "NOTICE %s :*** Notice -- Server is currently split, channel modes are limited",
		     nick);
	}

      nextping = CurrentTime;
#endif

    }
  else if (IsServer(cptr))
    {
      aClient *acptr;
      if ((acptr = find_server(user->server)) && acptr->from != sptr->from)
	{
	  sendto_ops_flag(UMODE_DEBUG,
			  "Bad User [%s] :%s USER %s@%s %s, != %s[%s]",
			  cptr->name, nick, sptr->username,
			  sptr->host, user->server,
			  acptr->name, acptr->from->name);
	  sendto_one(cptr,
		     ":%s KILL %s :%s (%s != %s[%s] USER from wrong direction)",
		     me.name, sptr->name, me.name, user->server,
		     acptr->from->name, acptr->from->host);
	  sptr->flags |= FLAGS_KILLED;
	  return exit_client(sptr, sptr, &me, "USER server wrong direction");

	}
      /*
       * Super GhostDetect:
       *        If we can't find the server the user is supposed to be on,
       * then simply blow the user away.        -Taner
       */
      if (!acptr)
	{
	  sendto_one(cptr,
		     ":%s KILL %s :%s GHOST (no server %s on the net)",
		     me.name, sptr->name, me.name, user->server);
	  sendto_ops_flag(UMODE_SERVNOTICE,
			  "No server %s for user %s[%s@%s] from %s",
			  user->server, sptr->name, sptr->username,
			  sptr->host, sptr->from->name);
	  sptr->flags |= FLAGS_KILLED;
	  return exit_client(sptr, sptr, &me, "Ghosted Client");
	}
    }

  send_umode(NULL, sptr, 0, ubuf);
  if (!*ubuf)
    {
      ubuf[0] = '+';
      ubuf[1] = '\0';
    }

  /* LINKLIST 
   * add to local client link list -Dianora
   * I really want to move this add to link list
   * inside the if (MyConnect(sptr)) up above
   * but I also want to make sure its really good and registered
   * local client
   *
   * double link list only for clients, traversing
   * a small link list for opers/servers isn't a big deal
   * but it is for clients -Dianora
   */

  if (MyConnect(sptr))
    {
      if (local_cptr_list)
	local_cptr_list->previous_local_client = sptr;
      sptr->previous_local_client = (aClient *) NULL;
      sptr->next_local_client = local_cptr_list;
      local_cptr_list = sptr;
    }

  sendto_serv_butone(cptr, "NICK %s %d %lu %s %s %s %s %lu :%s",
		     nick, sptr->hopcount + 1, sptr->tsinfo, ubuf,
		     sptr->username, sptr->host, user->server,
		     (unsigned long) sptr->ip.s_addr, sptr->info);
  if (ubuf[1])
    send_umode_out(cptr, sptr, 0);
  return 0;
}

/*
 * nickkilldone
 *
 * input        - pointer to physical aClient
 *              - pointer to source aClient
 *              - argument count
 *              - arguments
 *              - newts time
 *              - nick
 * output       -
 * side effects -
 */

int
nickkilldone(struct Client *cptr, struct Client *sptr, int parc,
	     char *parv[], time_t newts, char *nick)
{
  Link *channels;
  umode_t setflags;
  if (IsServer(sptr))
    ClearBitfield(setflags);
  else
    CopyUmodes(setflags, sptr->umodes);

  if (IsServer(sptr))
    {
      /* A server introducing a new client, change source */

      sptr = make_client(cptr);
      add_client_to_list(sptr);	/* double linked list */
      if (parc > 2)
	sptr->hopcount = atoi(parv[2]);
      if (newts)
	sptr->tsinfo = newts;
      else
	{
	  newts = sptr->tsinfo = CurrentTime;
	  ts_warn("Remote nick %s (%s) introduced without a TS", nick,
		  parv[0]);
	}
      /* copy the nick in place */
      (void) strcpy(sptr->name, nick);
      (void) add_to_client_hash_table(nick, sptr);
      if (parc > 9)
	{
	  int flag;
	  char *m;

	  /*
	     ** parse the usermodes -orabidoo
	   */
	  m = &parv[4][1];
	  while (*m)
	    {
	      flag = user_modes_from_c_to_bitmask[(unsigned char) *m];
	      if (flag == UMODE_INVISIBLE)
		{
		  Count.invisi++;
		}
	      if (flag == UMODE_OPER)
		{
		  Count.oper++;
		}
	      /*              if (TestBit(send_umodes, flag)) */
	      SetBit(sptr->umodes, flag);
	      m++;
	    }

	  return do_user(nick, cptr, sptr, parv[5], parv[6],
			 parv[7], parv[8], parv[9]);
	}
    }
  else if (sptr->name[0])
    {
      /*
         ** Client just changing his/her nick. If he/she is
         ** on a channel, send note of change to all clients
         ** on that channel. Propagate notice to other servers.
       */
      if (irccmp(parv[0], nick))
	sptr->tsinfo = newts ? newts : CurrentTime;

      if (MyConnect(sptr) && IsRegisteredUser(sptr))
	{
	  for (channels = sptr->user->channel; channels;
	       channels = channels->next)
	    {
	      if (can_send(sptr, channels->value.chptr) != 0)
		{
		  /* Cannot send to that channel, so cannot change nicks */
		  sendto_one(sptr, form_str(ERR_BANNICKCHANGE),
			     me.name, sptr->name, nick);
		  return 0;
		}
	    }

#ifdef ANTI_NICK_FLOOD

	  if ((sptr->last_nick_change + MAX_NICK_TIME) < CurrentTime)
	    sptr->number_of_nick_changes = 0;
	  sptr->last_nick_change = CurrentTime;
	  sptr->number_of_nick_changes++;

	  if ((sptr->number_of_nick_changes <= MAX_NICK_CHANGES)
	      || NoFloodProtection(sptr))
	    {
#endif
	      sendto_local_ops_flag(UMODE_NCHANGE,
				    "Nick change: From %s to %s [%s@%s]",
				    parv[0], nick, sptr->username,
				    sptr->host);

	      ClearUmode(sptr, UMODE_IDENTIFIED);
	      send_umode_out(cptr, sptr, setflags);

	      sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
	      if (sptr->user)
		{
		  add_history(sptr, 1);

		  sendto_serv_butone(cptr, ":%s NICK %s :%lu",
				     parv[0], nick, sptr->tsinfo);
		}
#ifdef ANTI_NICK_FLOOD
	    }
	  else
	    {
	      sendto_one(sptr,
			 ":%s NOTICE %s :*** Notice -- Too many nick changes wait %d seconds before trying to change it again.",
			 me.name, sptr->name, MAX_NICK_TIME);
	      sendto_ops_flag(UMODE_BOTS, "Flooder %s [%s@%s] on %s (nick)",
			      sptr->name, sptr->username, sptr->host,
			      me.name);

	      return 0;
	    }
#endif
	}
      else
	{
	  ClearUmode(sptr, UMODE_IDENTIFIED);
	  send_umode_out(cptr, sptr, setflags);
	  sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick);
	  if (sptr->user)
	    {
	      add_history(sptr, 1);
	      sendto_serv_butone(cptr, ":%s NICK %s :%lu",
				 parv[0], nick, sptr->tsinfo);
	      sendto_local_ops_flag(UMODE_NCHANGE,
				    "Nick change: From %s to %s [%s@%s]",
				    parv[0], nick, sptr->username,
				    sptr->host);
	    }
	}
    }
  else
    {
      /* Client setting NICK the first time */
      CopyUmodes(sptr->allowed_umodes, user_umodes);


      /* This had to be copied here to avoid problems.. */
      strcpy(sptr->name, nick);
      sptr->tsinfo = CurrentTime;
      if (sptr->user)
	{
	  char buf[USERLEN + 1];
	  strncpy_irc(buf, sptr->username, USERLEN);
	  buf[USERLEN] = '\0';
	  /*
	     ** USER already received, now we have NICK.
	     ** *NOTE* For servers "NICK" *must* precede the
	     ** user message (giving USER before NICK is possible
	     ** only for local client connection!). register_user
	     ** may reject the client and call exit_client for it
	     ** --must test this and exit m_nick too!!!
	   */
	  if (register_user(cptr, sptr, nick, buf) == CLIENT_EXITED)
	    return CLIENT_EXITED;
	}
    }

  /*
     **  Finally set new nick name.
   */
  if (sptr->name[0])
    del_from_client_hash_table(sptr->name, sptr);
  strcpy(sptr->name, nick);
  add_to_client_hash_table(nick, sptr);

  return 0;
}

