#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define	TELOPTS
#include <arpa/telnet.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#ifndef sun
#include <fcntl.h>
#endif
#ifdef aix
#include <sys/select.h>
#endif
#include "lint.h"
#include "interpret.h"
#include "comm.h"
#include "object.h"
#include "config.h"
#include "sent.h"

#ifndef __STRICT_BSD__
#define __STRICT_BSD__
#endif

int socket PROT ((int, int, int));
int bind PROT ((int, struct sockaddr *, int));
int listen PROT ((int, int));
int accept PROT ((int, struct sockaddr *, int *));
int select PROT ((int, fd_set *, fd_set *, fd_set *, struct timeval *));
int getpeername PROT ((int, struct sockaddr *, int *));

void bzero PROT ((char *, int));
void telnet_neg PROT ((char *, char *));
void shutdown (), set_prompt PROT ((char *));

extern char *xalloc (), *string_copy (), *unshared_str_copy ();
extern int d_flag;
extern int current_time;
char *first_cmd_in_buf PROT ((struct interactive *));
void next_cmd_in_buf PROT ((struct interactive *));
char *skip_eols PROT ((struct interactive *, char *));

extern void remove_interactive (), add_ref ();

extern struct value *clone_object ();

extern void logon (), debug_message (), fatal (), free_sentence ();

struct interactive *all_players[MAX_PLAYERS];

extern int errno;

void new_player ();

fd_set readfds;
int nfds = 0;
int num_player;

/*
 * Interprocess communication interface to the backend.
 */

static s;
extern int port_number;

void 
prepare_ipc ()
{
  struct sockaddr_in sin;
  struct hostent *hp;
  int tmp;
  char host_name[100];

  if (gethostname (host_name, sizeof host_name) == -1)
    {
      perror ("gethostname");
      fatal ("Error in gethostname()\n");
    }
  hp = gethostbyname (host_name);
  if (hp == 0)
    {
      (void) fprintf (stderr, "gethostbyname: unknown host.\n");
      exit (1);
    }
  memset ((char *) &sin, '\0', sizeof sin);
  memcpy ((char *) &sin.sin_addr, hp->h_addr, hp->h_length);
  sin.sin_port = htons (port_number);
  sin.sin_family = hp->h_addrtype;
  sin.sin_addr.s_addr = INADDR_ANY;
  s = socket (hp->h_addrtype, SOCK_STREAM, 0);
  if (s == -1)
    {
      perror ("socket");
      abort ();
    }
  tmp = 1;
  if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
		  (char *) &tmp, sizeof (tmp)) < 0)
    {
      perror ("setsockopt");
      exit (1);
    }
  if (bind (s, (struct sockaddr *) & sin, sizeof sin) == -1)
    {
      if (errno == EADDRINUSE)
	{
	  debug_message ("Socket already bound!\n");
	  exit (errno);
	}
      else
	{
	  perror ("bind");
	  abort ();
	}
    }
  if (listen (s, 5) == -1)
    {
      perror ("listen");
      abort ();
    }
  tmp = 1;
#ifdef sun
  if (ioctl (s, FIONBIO, &tmp) == -1)
    {
      perror ("ioctl socket FIONBIO");
      abort ();
    }
#else /* sun */
  if (fcntl (s, F_SETFL, FNDELAY) == -1)
    {
      perror ("ioctl socket FIONBIO");
      abort ();
    }
#endif /* sun */
  signal (SIGPIPE, SIG_IGN);
}

/*
 * This one is called when shutting down the MUD.
 */
void 
ipc_remove ()
{
  (void) printf ("Shutting down ipc...\n");
  close (s);
}

/*
 * Do not call remove_interactive() when write fails, as this may cause
 * command_giver to point to a deallocated object, which some callers
 * of add_message() can't handle.
 */
/*VARARGS1*/
void 
add_message (fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
     char *fmt;
     int a1, a2, a3, a4, a5, a6, a7, a8, a9;
{
  char buff[10000];		/* Kludgy! Hope this is enough ! */
  char buff2[sizeof buff];
  struct interactive *ip;
  int n, offset, chunk, length;
  int from, to;

  if (command_giver == 0 || command_giver->destructed)
    return;
  ip = command_giver->interactive;
  if (ip == 0 || ip->do_close)
    return;
  if (ip->out_portal)
    {
      buff[0] = ']';
      (void) sprintf (buff + 1, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
    }
  else
    {
      (void) sprintf (buff, fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
    }
  length = strlen (buff);
  /*
   * Always check that your arrays are big enough ! :-)
   */
  if (length > sizeof buff)
    fatal ("To long message!\n");
  if (ip->snoop_by)
    {
      struct object *save = command_giver;
      command_giver = ip->snoop_by->ob;
      add_message ("%%%s", buff);
      command_giver = save;
    }
  if (d_flag)
    debug_message ("[%s(%d)]: %s", command_giver->name, length, buff);
  /*
   * Insert CR after all NL.
   */
  for (from = 0, to = 0; to < sizeof buff2 && buff[from] != '\0';)
    {
      if (buff[from] == '\n')
	buff2[to++] = '\r';
      buff2[to++] = buff[from++];
    }
  buff2[to++] = '\0';
  length = to - 1;
  /*
   * We split up the message into something smaller than the max size.
   */
  for (offset = 0; length > 0; offset += chunk, length -= chunk)
    {
      chunk = length;
      if (chunk > MAX_SOCKET_PACKET_SIZE)
	chunk = MAX_SOCKET_PACKET_SIZE;
      if ((n = write (ip->socket, buff2 + offset, chunk)) == -1)
	{
	  if (errno == EMSGSIZE)
	    {
	      fprintf (stderr, "comm1: write EMSGSIZE.\n");
	      return;
	    }
	  if (errno == EINVAL)
	    {
	      fprintf (stderr, "comm1: write EINVAL.\n");
	      ip->do_close = 1;
	      return;
	    }
	  if (errno == ENETUNREACH)
	    {
	      fprintf (stderr, "comm1: write ENETUNREACH.\n");
	      ip->do_close = 1;
	      return;
	    }
	  if (errno == EHOSTUNREACH)
	    {
	      fprintf (stderr, "comm1: write EHOSTUNREACH.\n");
	      ip->do_close = 1;
	      return;
	    }
	  if (errno == EPIPE)
	    {
	      fprintf (stderr, "comm1: write EPIPE detected\n");
	      ip->do_close = 1;
	      return;
	    }
	  if (errno == EWOULDBLOCK)
	    {
	      fprintf (stderr, "comm1: write EWOULDBLOCK. Message discarded.\n");
	      ip->do_close = 1;
	      return;
	    }
	  fprintf (stderr, "write: unknown errno %d\n", errno);
	  perror ("write");
	  ip->do_close = 1;
	  return;
	}
      if (n != chunk)
	fprintf (stderr, "write socket: Size %d(%d) is to big !\n",
		 chunk, n);
      continue;
    }
}

/*
 * Copy a string, replacing newlines with '\0'. Also add an extra
 * space and back space for every newline. This trick will allow
 * otherwise empty lines, as multiple newlines would be replaced by
 * multiple zeroes only.
 */
static int 
copy_chars (from, to, n)
     char *from, *to;
     int n;
{
  int i;
  char *start = to;
  for (i = 0; i < n; i++)
    {
      if (from[i] == '\r')
	continue;
      if (from[i] == '\n')
	{
	  *to++ = ' ';
	  *to++ = '\b';
	  *to++ = '\0';
	  continue;
	}
      *to++ = from[i];
    }
  return to - start;
}

/*
 * Get a message from any player.  For all players without a completed
 * cmd in their input buffer, read their socket.  Then, return the first
 * cmd of the next player in sequence that has a complete cmd in their buffer.
 * CmdsGiven is used to allow people in ED to send more cmds (if they have
 * them queued up) than normal players.  If we get a heartbeat, still read
 * all sockets; if the next cmd giver is -1, we have already cycled and
 * can go back to do the heart beat.
 */

#define	StartCmdGiver	(MAX_PLAYERS-1)	/* the one after heartbeat */
#define IncCmdGiver	NextCmdGiver = (NextCmdGiver < 0? StartCmdGiver: \
					NextCmdGiver - 1)

int NextCmdGiver = StartCmdGiver;
int CmdsGiven = 0;		/* -1 is used to poll heart beat. */
int twait = 0;			/* wait time for select() */
extern int time_to_call_heart_beat, comm_time_to_call_heart_beat;

int 
get_message (buff, size)
     char *buff;
     int size;
{
  int i, res;
  struct interactive *ip = 0;
  char *p, *p2;

  /*
   * Stay in this loop until we have a message from a player.
   */
  while (1)
    {
      int new_socket;
      struct sockaddr_in addr;
      int length;
      struct timeval timeout;

      /* First, try to get a new player... */
      length = sizeof addr;
      new_socket = accept (s, (struct sockaddr *) & addr, &length);
      if (new_socket != -1)
	new_player (new_socket, &addr, length);
      else if (new_socket == -1 && errno != EWOULDBLOCK && errno != EINTR)
	{
	  perror ("accept");
	  abort ();
	}

      nfds = 0;
      FD_ZERO (&readfds);
      for (i = 0; i < MAX_PLAYERS; i++)
	{
	  ip = all_players[i];
	  if (!ip)
	    continue;
	  if (ip->do_close)
	    {
	      ip->do_close = 0;
	      remove_interactive (ip->ob);
	      continue;
	    }
	  if (!first_cmd_in_buf (ip))
	    {
	      FD_SET (ip->socket, &readfds);
	      if (ip->socket >= nfds)
		nfds = ip->socket + 1;
	      if (ip->out_portal)
		{
		  FD_SET (ip->portal_socket, &readfds);
		  if (ip->portal_socket >= nfds)
		    nfds = ip->portal_socket + 1;
		}
	    }
	}
      timeout.tv_sec = twait;	/* avoid busy waiting when no buffered cmds */
      timeout.tv_usec = 0;
      res = select (nfds, &readfds, 0, 0, &timeout);
      if (res == -1)
	{
	  twait = 0;
	  if (errno == EINTR)	/* if we got an alarm, finish the round */
	    goto return_next_command;
	  perror ("select");
	  return 0;
	}
      if (res)
	{			/* waiting packets */
	  for (i = 0; i < MAX_PLAYERS; i++)
	    {			/* read all pending sockets */
	      struct interactive *ip = all_players[i];
	      int x;
	      if (ip == 0)
		continue;
	      if (FD_ISSET (ip->socket, &readfds))
		{		/* read this player */
		  char *p;
		  int l;

		  /* dont overfill their buffer */
		  l = MAX_TEXT - ip->text_end - 1;
		  if (l < size)
		    size = l;

		  if ((l = read (ip->socket, buff, size)) == -1)
		    {
		      if (errno == ENETUNREACH)
			{
			  debug_message ("Net unreachable detected.\n");
			  remove_interactive (ip->ob);
			  continue;
			}
		      if (errno == EHOSTUNREACH)
			{
			  debug_message ("Host unreachable detected.\n");
			  remove_interactive (ip->ob);
			  continue;
			}
		      if (errno == ETIMEDOUT)
			{
			  debug_message ("Connection timed out detected.\n");
			  remove_interactive (ip->ob);
			  continue;
			}
		      if (errno == ECONNRESET)
			{
			  debug_message ("Connection reset by peer detected.\n");
			  remove_interactive (ip->ob);
			  continue;
			}
		      if (errno == EWOULDBLOCK)
			{
			  debug_message ("read would block socket %d!\n",
					 ip->socket);
			  remove_interactive (ip->ob);
			  continue;
			}
		      if (errno == EMSGSIZE)
			{
			  debug_message ("read EMSGSIZE !\n");
			  continue;
			}
		      perror ("read");
		      debug_message ("Unknown errno %d\n", errno);
		      remove_interactive (ip->ob);
		      continue;
		    }
		  /*
	           * IF the data goes through a portal, send it,
	           * but don't return any data.
	           */
		  if (ip->out_portal)
		    {
		      if (ip->text_end)
			{	/* pending text first */
			  write (ip->portal_socket, ip->text, ip->text_end);
			  ip->text_end = 0;
			}
		      if (l)
			write (ip->portal_socket, buff, l);
		      continue;
		    }
		  if (l == 0)
		    {
		      if (ip->closing)
			fatal ("Tried to read from closing socket.\n");
		      remove_interactive (ip->ob);
		      return 0;
		    }
		  buff[l] = '\0';
		  /* replace newlines by nulls and catenate to buffer */
		  ip->text_end +=
		    copy_chars (buff, ip->text + ip->text_end, l);
#if 0
		  for (x = 0; x < l; x++)
		    {
		      if (buff[x] == '\n' || buff[x] == '\r')
			buff[x] = '\0';
		    }
		  memcpy (ip->text + ip->text_end, buff, l + 1);
		  ip->text_end += l;
#endif
		  /* now, text->end is just after the last char read.  If last */
		  /* char was a nl, char *before* text_end will be null. */
		  ip->text[ip->text_end] = '\0';
		}
	    }
	}
      /*
       * we have read the sockets; now find and return a command
       */
    return_next_command:
      twait = 0;
      ip = 0;
      for (i = 0; i < MAX_PLAYERS + 1; i++)
	{
	  if (NextCmdGiver == -1)
	    {			/* we have cycled around all players */
	      CmdsGiven = 0;	/* check heart beat */
	      IncCmdGiver;
	      if (comm_time_to_call_heart_beat)
		{
		  time_to_call_heart_beat = 1;	/* twait stays 0! */
		  return 0;
		}
	    }
	  ip = all_players[NextCmdGiver];
	  if (ip && (p = first_cmd_in_buf (ip)))	/* wont respond to partials */
	    break;
	  CmdsGiven = 0;	/* new player, no cmds issued */
	  IncCmdGiver;
	}

      if ((!ip) || !p)
	{			/* no cmds found; loop and select (on timeout) again */
	  if (comm_time_to_call_heart_beat)
	    {			/* may as well do it now */
	      time_to_call_heart_beat = 1;	/* no cmds, do heart beat */
	      NextCmdGiver = StartCmdGiver;	/* do a complete poll next time */
	      CmdsGiven = 0;
	      return (0);
	    }
	  /* no heart beat to do and no cmds pending - avoid busy wait on select */
	  twait = 1;
	  continue;		/* else await another cmd */
	}

      /*
       * we have a player cmd - return it.  If he is in ed, count his
       * cmds, else only allow 1 cmd.  If he has only one partially
       * completed cmd left after * this, move it to the start of his
       * buffer; new stuff will be appended.
       */

      command_giver = ip->ob;
      telnet_neg (buff, p);
      next_cmd_in_buf (ip);	/* move on buffer pointers */

      /* if he is not in ed, dont let him issue another till the poll comes again */

      if (command_giver->ed_buffer && (CmdsGiven < ALLOWED_ED_CMDS))
	CmdsGiven++;
      else
	{
	  IncCmdGiver;
	  CmdsGiven = 0;
	}

      /* manage snooping - should the snooper see type ahead?  Well, he doesn't here
       */
      if (ip->snoop_by && !ip->noecho)
	{
	  command_giver = ip->snoop_by->ob;
	  add_message ("%% %s\n", buff);
	}
      command_giver = ip->ob;
      if (ip->noecho)
	{
	  /* Must not enable echo before the user input is received. */
	  add_message ("%c%c%c", IAC, WONT, TELOPT_ECHO);
	}
      ip->noecho = 0;
      ip->last_time = current_time;
      return 1;
    }
}

/*
 * find the first character of the next complete cmd in a buffer, 0 if no
 * completed cmd.  There is a completed cmd if there is a null between
 * text_start and text_end.  Zero length commands are discarded (as occur
 * between <cr> and <lf>).  Update text_start if we have to skip leading
 * nulls.
 */

char *
first_cmd_in_buf (ip)
     struct interactive *ip;
{
  char *p, *q;

  p = ip->text_start + ip->text;

  while ((p < (ip->text_end + ip->text)) && !*p)	/* skip null input */
    p++;

  ip->text_start = p - ip->text;

  if (ip->text_start >= ip->text_end)
    {
      ip->text_start = ip->text_end = 0;
      ip->text[0] = '\0';
      return (0);
    }

  while ((p < (ip->text_end + ip->text)) && *p)	/* find end of cmd */
    p++;

  if (p < ip->text + ip->text_end)	/* null terminated, was command */
    return (ip->text + ip->text_start);

/* have a partial command at end of buffer; move it to start, return null */
/* if it can't move down, truncate it and return it as cmd. */

  p = ip->text + ip->text_start;
  q = ip->text;
  while (p < (ip->text + ip->text_end))
    *(q++) = *(p++);

  ip->text_end -= ip->text_start;
  ip->text_start = 0;
  if (ip->text_end > MAX_TEXT - 2)
    {
      ip->text[ip->text_end - 2] = '\0';	/* nulls to truncate */
      ip->text[ip->text_end - 1] = '\0';	/* nulls to truncate */
      ip->text_end--;
      return (ip->text);
    }
/* buffer not full and no newline - no cmd. */
  return (0);
}

/*
 * move pointers to next cmd, or clear buf.
 */

void 
next_cmd_in_buf (ip)
     struct interactive *ip;
{
  char *p = ip->text + ip->text_start;
  while (*p && p < ip->text + ip->text_end)
    p++;
  /* skip past any nulls at the end */
  while (!*p && p < ip->text + ip->text_end)
    p++;
  if (p < ip->text + ip->text_end)
    ip->text_start = p - ip->text;
  else
    {
      ip->text_start = ip->text_end = 0;
      ip->text[0] = '\0';
    }
}


/*
 * Remove an interactive player immediately.
 */
void 
remove_interactive (ob)
     struct object *ob;
{
  struct object *save = command_giver;
  int i;

  for (i = 0; i < MAX_PLAYERS; i++)
    {
      if (all_players[i] != ob->interactive)
	continue;
      if (ob->interactive->closing)
	fatal ("Double call to remove_interactive()\n");
      ob->interactive->closing = 1;
      if (ob->interactive->snoop_by)
	{
	  ob->interactive->snoop_by->snoop_on = 0;
	  ob->interactive->snoop_by = 0;
	}
      if (ob->interactive->snoop_on)
	{
	  ob->interactive->snoop_on->snoop_by = 0;
	  ob->interactive->snoop_on = 0;
	}
      command_giver = ob;
      add_message ("Closing down.\n");
      (void) shutdown (ob->interactive->socket, 2);
      close (ob->interactive->socket);
      num_player--;
      if (ob->interactive->input_to)
	{
	  free_object (ob->interactive->input_to->ob, "remove_interactive");
	  free_sentence (ob->interactive->input_to);
	  ob->interactive->input_to = 0;
	}
      free ((char *) ob->interactive);
      ob->interactive = 0;
      all_players[i] = 0;
      free_object (ob, "remove_interactive");
      command_giver = save;
      return;
    }
  (void) fprintf (stderr, "Could not find and remove player %s\n", ob->name);
  abort ();
}

int
allow_host_access (new_socket)
     int new_socket;
{
  struct sockaddr_in apa;
  int len;
  char *ipname, *calloc (), *xalloc ();
  static int read_access_list = 0;
  static struct access_list
  {
    int addr_len;
    char *addr, *name, *comment;
    struct access_list *next;
  } *access_list;
  register struct access_list *ap;

  if (!read_access_list)
    {
      FILE *f = fopen ("ACCESS.DENY", "r");
      char buf[1024], ipn[50], hname[100], comment[1024], *p1, *p2;
      struct access_list *na;
      struct hostent *hent;

      read_access_list = 1;
      if (f)
	{
	  while (fgets (buf, sizeof buf - 1, f))
	    {
	      if (*buf != '#')
		{
		  ipn[0] = hname[0] = comment[0] = 0;
		  if (p1 = index (buf, ':'))
		    *p1 = 0;
		  if (buf[0] && buf[0] != '\n')
		    strncpy (ipn, buf, sizeof ipn - 1);
		  if ((p2 = p1) && *++p2)
		    {
		      if (p1 = index (p2, ':'))
			*p1 = 0;
		      if (p2[0] && p2[0] != '\n')
			strcpy (hname, p2);
		      if (p1 && p1[1] && p1[1] != '\n')
			strcpy (comment, p1 + 1);
		    }

		  if (!(na = (struct access_list *) xalloc (sizeof na[0])))
		    {
		      fatal ("Out of mem.\n");
		    }
		  na->addr = na->name = na->comment = 0;
		  na->next = 0;
		  if (*ipn && (!(na->addr = xalloc (strlen (ipn) + 1)) ||
			       !strcpy (na->addr, ipn)))
		    fatal ("Out of mem.\n");
		  if (*hname && (!(na->name = xalloc (strlen (hname) + 1)) ||
				 !strcpy (na->name, hname)))
		    fatal ("Out of mem.\n");
		  if (*comment && (!(na->comment = xalloc (strlen (comment) + 1)) ||
				   !strcpy (na->comment, comment)))
		    fatal ("Out of mem.\n");

		  if ((!(int) *ipn)
		      &&
		      ((!*hname)
		       || (!(hent = gethostbyname (hname))) ||
		       (!(na->addr =
			  xalloc (hent->h_length + 1))) ||
		       !strcpy ((char *) na->addr,
				inet_ntoa (*(long *) hent->h_addr))))
		    {
		      if (na->name)
			free (na->name);
		      if (na->comment)
			free (na->comment);
		      free ((char *) na);
		      continue;
		    }
		  if (!(na->addr_len = strlen (na->addr)))
		    continue;

		  /* printf("disabling: %s:%s:%s\n", na->addr,
		         na->name?na->name:"no name",
		         na->comment?na->comment:"no comment");  */

		  na->next = access_list;
		  access_list = na;
		}
	    }
	  fclose (f);
	}
    }

  if (!access_list)
    return 0;

  if (getpeername (new_socket, (struct sockaddr *) & apa, &len) == -1)
    {
      perror ("getpeername");
      return -1;
    }

  ipname = inet_ntoa (apa.sin_addr);

  for (ap = access_list; ap; ap = ap->next)
    if (!strncmp (ipname, ap->addr, ap->addr_len))
      {
	if (ap->comment)
	  (void) write (new_socket, ap->comment,
			strlen (ap->comment));
	printf ("Stopping: %s:%s\n", ap->addr, ap->name ? ap->name : "no name");
	close (new_socket);
	return -1;
      }
  return 0;
}

/*
 * get the I'th player object from the interactive list, i starts at 0
 * and can go to num_player - 1.  For users(), etc.
 */
struct object *
get_interactive_object (i)
     int i;
{
  int n;

  if (i >= num_player)		/* love them ASSERTS() :-) */
    fatal ("Get interactive (%d) with only %d players!", i, num_player);

  for (n = 0; n < MAX_PLAYERS; n++)
    if (all_players[n])
      if (!(i--))
	return (all_players[n]->ob);

  fatal ("Get interactive: player %d not found! (num_players = %d)",
	 i, num_player);
}

void 
new_player (new_socket, addr, len)
     int new_socket;
     struct sockaddr_in *addr;
     int len;
{
  int i;
  char *p;

  if (allow_host_access (new_socket))
    return;
  if (d_flag)
    debug_message ("New player at socket %d.\n", new_socket);
  for (i = 0; i < MAX_PLAYERS; i++)
    {
      struct object *ob;

      if (all_players[i] != 0)
	continue;
      current_object = 0;
      ob = (clone_object ("obj/player"))->u.ob;
      if (ob == 0)
	fatal ("Could not load 'obj/player'\n");
      add_ref (ob, "new_player");
      ob->interactive =
	(struct interactive *) xalloc (sizeof (struct interactive));
      ob->interactive->default_err_message = 0;
      ob->once_interactive = 1;
      all_players[i] = ob->interactive;
      command_giver = ob;
      ob->interactive->ob = ob;
      ob->interactive->text[0] = '\0';
      ob->interactive->input_to = 0;
      ob->interactive->closing = 0;
      ob->interactive->snoop_on = 0;
      ob->interactive->snoop_by = 0;
      ob->interactive->out_portal = 0;
      ob->interactive->portal_socket = 0;
      ob->interactive->from_portal = 0;
      ob->interactive->text_end = 0;
      ob->interactive->text_start = 0;
      ob->interactive->do_close = 0;
      ob->interactive->noecho = 0;
      ob->interactive->last_time = current_time;
      set_prompt ("> ");
      all_players[i]->socket = new_socket;
      /* memcpy(&all_players[i]->addr, addr, len); */
      getpeername (new_socket, (struct sockaddr *) & all_players[i]->addr,
		   &len);
      num_player++;
      logon (ob);
      return;
    }
  p = "Lpmud is full. Come back later.\r\n";
  write (new_socket, p, strlen (p));
  close (new_socket);
}

int 
call_function_interactive (i, str)
     struct interactive *i;
     char *str;
{
  char *function;
  struct object *ob;
  struct value *val;

  if (!i->input_to)
    return 0;
  /*
   * Special feature: input_to() has been called to setup
   * a call to a function.
   */
  if (i->input_to->ob->destructed)
    {
      /* Sorry, the object has selfdestructed ! */
      free_object (i->input_to->ob, "call_function_interactive");
      free_sentence (i->input_to);
      i->input_to = 0;
      return 0;
    }
  free_object (i->input_to->ob, "call_function_interactive");
  function = string_copy (command_giver->interactive->input_to->function);
  ob = i->input_to->ob;
  val = make_string (str);
  free_sentence (i->input_to);
  /*
   * We must clear this reference before the call to apply(), because
   * someone might want to set up a new input_to().
   */
  i->input_to = 0;
  /*
   * Now we set current_object to this object, so that input_to will
   * work for static functions.
   */
  current_object = ob;
  (void) apply (function, ob, val);
  free (function);
  return 1;
}

int 
set_call (ob, sent, noecho)
     struct object *ob;
     struct sentence *sent;
     int noecho;
{
  struct object *save = command_giver;
  if (ob == 0 || sent == 0)
    return 0;
  if (ob->interactive == 0 || ob->interactive->input_to)
    return 0;
  ob->interactive->input_to = sent;
  ob->interactive->noecho = noecho;
  command_giver = ob;
  if (noecho)
    add_message ("%c%c%c", IAC, WILL, TELOPT_ECHO);
  command_giver = save;
  return 1;
}

void 
show_info_about (str, room, i)
     char *str, *room;
     struct interactive *i;
{
  struct hostent *hp = 0;

#if 0
  hp = gethostbyaddr (&i->addr.sin_addr.s_addr, 4, AF_INET);
#endif
  add_message ("%-15s %-15s %s\n",
	       hp ? hp->h_name : inet_ntoa (i->addr.sin_addr), str, room);
}

void 
remove_all_players ()
{
  int i;

  for (i = 0; i < MAX_PLAYERS; i++)
    {
      if (all_players[i] == 0)
	continue;
      command_giver = all_players[i]->ob;
      (void) apply ("quit", all_players[i]->ob, 0);
    }
}

void 
set_prompt (str)
     char *str;
{
  command_giver->interactive->prompt = str;
}

/*
 * Print the prompt, but only if input_to not is enabled.
 */
void 
print_prompt ()
{
  if (command_giver == 0)
    fatal ("command_giver == 0.\n");
  if (command_giver->interactive->input_to == 0)
    add_message (command_giver->interactive->prompt);
}

void 
set_snoop (me, you)
     struct object *me, *you;
{
  struct interactive *on = 0, *by = 0;
  int i;

  for (i = 0; i < MAX_PLAYERS && (on == 0 || by == 0); i++)
    {
      if (all_players[i] == 0)
	continue;
      if (all_players[i]->ob == me)
	by = all_players[i];
      else if (all_players[i]->ob == you)
	on = all_players[i];
    }
  if (you == 0)
    {
      if (by == 0)
	fatal ("Could not find myself to stop snoop.\n");
      add_message ("Ok.\n");
      if (by->snoop_on == 0)
	return;
      by->snoop_on->snoop_by = 0;
      by->snoop_on = 0;
      return;
    }
  if (on == 0 || by == 0)
    {
      add_message ("Failed.\n");
      return;
    }
  if (by->snoop_on)
    by->snoop_on->snoop_by = 0;
  if (on->snoop_by)
    {
      add_message ("Busy.\n");
      return;
    }
  on->snoop_by = by;
  by->snoop_on = on;
  add_message ("Ok.\n");
}

#define	TS_DATA		0
#define	TS_IAC		1
#define	TS_WILL		2
#define	TS_WONT		3
#define	TS_DO		4
#define	TS_DONT		5

void 
telnet_neg (to, from)
     char *to, *from;
{
  int state = TS_DATA;
  int ch;
  char *first = to;

  while (1)
    {
      ch = (*from++ & 0xff);
      switch (state)
	{
	case TS_DATA:
	  switch (ch)
	    {
	    case IAC:
	      state = TS_IAC;
	      continue;
	    case '\b':		/* Backspace */
	    case 0x7f:		/* Delete */
	      if (to <= first)
		continue;
	      to -= 1;
	      continue;
	    default:
	      if (ch & 0x80)
		{
		  debug_message ("Tel_neg: 0x%x\n", ch);
		  continue;
		}
	      *to++ = ch;
	      if (ch == 0)
		return;
	      continue;
	    }
	case TS_IAC:
	  switch (ch)
	    {
	    case WILL:
	      state = TS_WILL;
	      continue;
	    case WONT:
	      state = TS_WONT;
	      continue;
	    case DO:
	      state = TS_DO;
	      continue;
	    case DONT:
	      state = TS_DONT;
	      continue;
	    case DM:
	      break;
	    case NOP:
	    case GA:
	    default:
	      break;
	    }
	  state = TS_DATA;
	  continue;
	case TS_WILL:
	  debug_message ("Will %s\n", telopts[ch]);
	  state = TS_DATA;
	  continue;
	case TS_WONT:
	  debug_message ("Wont %s\n", telopts[ch]);
	  state = TS_DATA;
	  continue;
	case TS_DO:
	  debug_message ("Do %s\n", telopts[ch]);
	  state = TS_DATA;
	  continue;
	case TS_DONT:
	  debug_message ("Dont %s\n", telopts[ch]);
	  state = TS_DATA;
	  continue;
	default:
	  debug_message ("Bad state: 0x%x\n", state);
	  state = TS_DATA;
	  continue;
	}
    }
}

#ifdef INET_NTOA_OK

char *
query_ip_number (ob)
     struct object *ob;
{
  if (ob == 0)
    ob = command_giver;
  if (!ob || ob->interactive == 0)
    return 0;
  return inet_ntoa (ob->interactive->addr.sin_addr);
}

#else /* INET_NTOA_OK */
/*
Note: if the address string is "a.b.c.d" the address number is
      a * 256^3 + b * 256^2 + c * 256 + d

*/

char *
query_ip_number (ob)
     struct object *ob;
{
  u_long s_ad;
  int a, b, c, d;
  static char addr[20];		/* 16 + 1 should be enough */

  if (ob == 0)
    ob = command_giver;
  if (!ob || ob->interactive == 0)
    return 0;
  s_ad = ob->interactive->addr.sin_addr.s_addr;
  d = s_ad % 256;
  s_ad /= 256;
  c = s_ad % 256;
  s_ad /= 256;
  b = s_ad % 256;
  a = s_ad / 256;
  sprintf (addr, "%d.%d.%d.%d", a, b, c, d);
  return addr;
}

#endif	/* INET_NTOA_OK */

char *
query_host_name ()
{
  static char name[20];

  gethostname (name, sizeof name);
  name[sizeof name - 1] = '\0';	/* Just to make sure */
  return name;
}

struct value *
query_snoop (ob)
     struct object *ob;
{
  struct value *arg1;
  if (ob->interactive->snoop_by == 0)
    return &const0;
  arg1 = alloc_value ();
  arg1->type = T_OBJECT;
  arg1->u.ob = ob->interactive->snoop_by->ob;
  add_ref (arg1->u.ob, "query_snoop");
  return arg1;
}

int 
query_idle (ob)
     struct object *ob;
{
  if (!ob->interactive)
    error ("query_idle() of non-interactive object.\n");
  return current_time - ob->interactive->last_time;
}

void 
notify_no_command ()
{
  char *p;

  if (!command_giver->interactive)
    return;
  p = command_giver->interactive->default_err_message;
  if (p)
    {
      add_message (p);
      free_string (p);
      command_giver->interactive->default_err_message = 0;
    }
  else
    {
      add_message ("What ?\n");
    }
}

void 
clear_notify ()
{
  if (!command_giver->interactive)
    return;
  if (command_giver->interactive->default_err_message)
    {
      free_string (command_giver->interactive->default_err_message);
      command_giver->interactive->default_err_message = 0;
    }
}

void 
set_notify_fail_message (str)
     char *str;
{
  clear_notify ();
  if (!command_giver || !command_giver->interactive)
    return;
  command_giver->interactive->default_err_message = make_shared_string (str);
}