pgplus/bin/
pgplus/help_files/
pgplus/port_redirector/
pgplus/src/configure/makefiles/
/*
 * Playground+ - socket.c
 * The socket handling code - keep away!
 * ---------------------------------------------------------------------------
 */

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#ifdef LINUX
#include "include/un.h"
#else
#include <sys/un.h>
#endif
#include <sys/time.h>
#include <sys/ioctl.h>
#if !defined(linux)
#include <sys/filio.h>
#endif

#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>

#define IPROTO 0

#include "include/config.h"
#include "include/player.h"
#include "include/fix.h"
#include "include/proto.h"
#include "include/output.h"

#ifdef IDENT
#include "include/ident.h"
extern void send_ident_request(player * p, struct sockaddr_in *sockadd);
extern int ident_toclient_fds[2];
extern void read_ident_reply(void);
#endif


/* externs */

#ifdef SOLARIS
extern int close(int);
extern int socket(int, int, int);

#if !defined(linux)
extern int getsockopt(int, int, int, char *, int *);
extern int setsockopt(int, int, int, char *, int);
extern void perror(char *);
extern void bzero(char *, int);
extern int select(int, fd_set *, fd_set *, fd_set *, struct timval *);
#endif

/* Used to be after the #endif */

#ifdef NEED_BIND_DECL
extern int bind(int, struct sockaddr *, int);
#endif /* NEED_BIND_DECL */
#ifdef NEED_LISTEN_DECL
extern int listen(int, int);
#endif /* NEED_LISTEN_DECL */

extern int write(int, char *, int);
extern int read(int, char *, int);
extern char *sprintf(char *, char *,...);

#endif /* SOLARIS */

/* interns */

int main_descriptor, alive_descriptor, oi_test;
file banish_file, banish_msg, full_msg, splat_msg, under18_msg;

/* terminal defintitions
   NOTE: The incorrect vt100 and ansi definitions have been corrected
   here - cheers blimey! */

struct terminal terms[] =
{
  {"xterm", "\033[1m", "\033[m", "\033[H\033[2J"},
  {"vt220", "\033[1m", "\033[m", "\033[H\033[J"},
  {"vt100", "\033[1m", "\033[m", "\033[;H\033[2J"},
  {"vt102", "\033[1m", "\033[m", "50\033["},
  {"ansi", "\033[1m", "\033[0m", "\033[;H\033[2J"},
  {"wyse-30", "\033G4", "\033G0", ""},
  {"tvi912", "\033l", "\033m", "\032"},
  {"sun", "\033[1m", "\033[m", "\014"},
  {"adm", "\033)", "\033(", "1\032"},
  {"hp2392", "\033&dB", "\033&d@", "\033H\033J"},
  {"", "", "", ""}
};


void lscripto(char *filename, char *string, int len)
{
  int fd;
  char path[160];
  char *oldstack = stack, *ptr;

  memset(path, 0, 160);
  sprintf(path, "logs/%s", filename);

#ifdef BSDISH
  fd = open(path, (O_WRONLY | O_CREAT | O_APPEND), (S_IRUSR | S_IWUSR));
#else
  fd = open(path, (O_WRONLY | O_CREAT | O_APPEND | O_SYNC), (S_IRUSR | S_IWUSR))
    ;
#endif

  strcpy(stack, string);
  stack = end_string(stack);
  for (ptr = oldstack; *ptr; ptr++)
    switch (*ptr)
    {
      case (char) IAC:
	*ptr = '#';
	break;
      case (char) GA:
      case (char) EOR:
	*ptr = '\n';
	break;
    }

  write(fd, oldstack, len);
  close(fd);
  stack = oldstack;

}

int valid_char_col(char ctest)
{

  ctest = tolower(ctest);

  if (ctest == 'a' || ctest == 'b' || ctest == 'c' ||
      ctest == 'g' || ctest == 'h' || ctest == 'i' ||
      ctest == 'k' || ctest == 'p' || ctest == 'r' ||
      ctest == 's' || ctest == 'u' || ctest == 'y' ||
      ctest == 'x')
    return 1;
  return 0;
}


char *getcolor(player * p, char col)
{
  static char ret[16];
  int rnd;

  if (p->misc_flags & NOCOLOR && !(p->misc_flags & SYSTEM_COLOR))
    return 0;
  switch (col)
  {
    case 'X':
      rnd = (rand() % 7) + 1;
      sprintf(ret, "\033[1;3%dm", rnd);
      return ret;
    case 'x':
      rnd = (rand() % 7) + 1;
      sprintf(ret, "\033[0;3%dm", rnd);
      return ret;
    case 'S':
    case 's':
      return "\033[3m";
      break;
    case 'K':
    case 'k':
      if (p->misc_flags & STOP_BAD_COLORS)
	return "";
      else
	return "\033[5m";
      break;
    case 'U':
    case 'u':
      return "\033[4m";
      break;
    case 'I':
    case 'i':
      if (p->misc_flags & STOP_BAD_COLORS)
	return "";
      else
	return "\033[7m";
      break;
    case 'H':
    case 'h':
      return "\033[0;1m";
      break;
    case 'r':
      return "\033[0;31m";
      break;
    case 'R':
      return "\033[1;31m";
      break;
    case 'y':
      return "\033[0;33m";
      break;
    case 'Y':
      return "\033[1;33m";
      break;
    case 'g':
      return "\033[0;32m";
      break;
    case 'G':
      return "\033[1;32m";
      break;
    case 'p':
      return "\033[0;35m";
      break;
    case 'P':
      return "\033[1;35m";
      break;
    case 'c':
      return "\033[0;36m";
      break;
    case 'C':
      return "\033[1;36m";
      break;
    case 'B':
      return "\033[1;34m";
      break;
    case 'b':
      return "\033[0;34m";
      break;
    case 'a':
      return "\033[1;30m";
      break;
    case 'A':
      return "\033[0;37m";
      break;
    case 'N':
    case 'n':
      if (p->misc_flags & SYSTEM_COLOR && valid_char_col(p->colorset[sys_color_atm]))
	return getcolor(p, p->colorset[sys_color_atm]);
      else if (command_type & HIGHLIGHT && p->term)
	return terms[(p->term) - 1].bold;
      else if (p->term)
	return terms[(p->term) - 1].off;
      else
	return 0;
      break;
    case '^':			/* unusable by the silly ressies ;-) */
      if (valid_char_col(p->colorset[SYSsc]))
	return getcolor(p, p->colorset[SYSsc]);
      else if (p->term)
	return terms[(p->term) - 1].off;
      else
	return 0;
      break;

    default:
      if (p->misc_flags & SYSTEM_COLOR)
      {
	if (valid_char_col(col))
	  return getcolor(p, p->colorset[sys_color_atm]);
	else if (command_type & HIGHLIGHT && p->term)
	  return terms[(p->term) - 1].bold;
	else if (p->term)
	  return terms[(p->term) - 1].off;
	else
	  return 0;
      }
      else if (command_type & HIGHLIGHT && p->term)
	return terms[(p->term) - 1].bold;
      else if (p->term)
	return terms[(p->term) - 1].off;
      else
	return 0;
      break;
  }
  return 0;
}
void clear_screen(player * p, char *str)
{

  if (p->term)
  {
  if (!write_socket(&p->fd, terms[(p->term) - 1].cls,strlen(terms[(p->term) - 1].cls)))
    quit(p, 0);
  }
  else
    tell_player(p, " You have to have a termtype set for this command to"
		" work. Use the command: hitells <termtype>\n");
}


/* the hitells command */

void hitells(player * p, char *str)
{
  char *oldstack;
  int i;

  oldstack = stack;

  if (!*str && !(p->term))
  {
    tell_player(p, " Format: Hitells <termtype/?/off>\n");
    return;
  }
  if (!*str)
  {
    sprintf(stack, " Hitells is on, with terminal set to %s.\n",
	    terms[(p->term) - 1].name);
    stack = end_string(stack);
    tell_player(p, oldstack);
    stack = oldstack;
    return;
  }
  if (*str == '?')
  {
    strcpy(stack, " Current terminal types available : ");
    stack = strchr(stack, 0);
    for (i = 0; terms[i].name[0] != '\0'; i++)
    {
      sprintf(stack, "%s, ", terms[i].name);
      stack = strchr(stack, 0);;
    }
    stack -= 2;
    *stack++ = '.';
    *stack++ = '\n';
    *stack++ = 0;
    tell_player(p, oldstack);
    stack = oldstack;
    return;
  }
  if (!strcasecmp("off", str))
  {
    /* p->term = 0; */
    tell_player(p, " Hitells turned off.\n");
    if (!(p->misc_flags & NOCOLOR))
    {
      tell_player(p, " Standard color mode disabled due to lack of hitells.\n");
      p->misc_flags |= NOCOLOR;
    }
    if (p->misc_flags & SYSTEM_COLOR)
    {
      tell_player(p, " System colors have been disabled because hitells are off.\n");
      p->misc_flags &= ~SYSTEM_COLOR;
    }
    p->term = 0;
    return;
  }
  for (i = 0; terms[i].name[0] != '\0'; i++)
    if (!strcasecmp(str, terms[i].name))
    {
      p->term = i + 1;
      sprintf(stack, " Hitells turned on, with terminal set to %s.\n",
	      terms[i].name);
      stack = end_string(stack);
      tell_player(p, oldstack);
      stack = oldstack;
      return;
    }
  sprintf(stack, " Terminal type '%s' not supported.\n"
	  " Do hitells '?' to list currently supported terminals.\n", str);
  stack = end_string(stack);
  tell_player(p, oldstack);
  stack = oldstack;
  return;
}

/* close down sockets after use */

void close_down_socket()
{
  shutdown(main_descriptor, 2);
  close(main_descriptor);
}

#ifdef INTERCOM
void close_only_main_fd(void)
{
  close(main_descriptor);
  return;
}
#endif

/* grab the main socket */

void init_socket(int port)
{
  struct sockaddr_in main_socket;
  int dummy = 1;
  char *oldstack;
  char *hostname;
  struct hostent *hp;

  oldstack = stack;

/*
   banish_file = load_file("files/banish");
   banish_msg = load_file("files/banish.msg");
   full_msg = load_file("files/full.msg");
 */

  /* grab the main socket */

  hostname = (char *) MALLOC(101);
  bzero((char *) &main_socket, sizeof(struct sockaddr_in));
  gethostname(hostname, 100);

  hp = gethostbyname(hostname);
  if (hp == NULL)
  {
    handle_error("Error: Host machine does not exist!\n");
  }
  main_socket.sin_family = hp->h_addrtype;
  main_socket.sin_port = htons(port);

  main_descriptor = socket(AF_INET, SOCK_STREAM, IPROTO);
  if (main_descriptor < 0)
  {
    log("boot", "Couldn't grab the socket!!!");
    exit(-1);
  }
  if (setsockopt(main_descriptor, SOL_SOCKET, SO_REUSEADDR, (char *) &dummy,
		 sizeof(dummy)) < 0)
    handle_error("Couldn't setsockopt()");

  if (ioctl(main_descriptor, FIONBIO, &dummy) < 0)
    handle_error("Can't set non-blocking");

  if (bind(main_descriptor, (struct sockaddr *) &main_socket, sizeof(main_socket)) < 0)
  {
    log("boot", "Couldn't bind socket - something is on that port number!");
    exit(-2);
  }
  if (listen(main_descriptor, 5) < 0)
    handle_error("Listen refused");

  sprintf(oldstack, "Main socket bound and listening on port %d", port);
  stack = end_string(oldstack);
  log("boot", oldstack);

  stack = oldstack;
}

/* tell the angel the server is alive */

void do_alive_ping()
{
  static int count = 5;

  count--;
  if (!count && alive_descriptor > 0)
  {
    count = 5;
    if (write(alive_descriptor, "SpAng!", 6) < 0)
    {
      if (errno != EPIPE)
	return;

      log("sigpipe", "Angel pipe closed");
      close(alive_descriptor);	/* try to clean up */
      alive_descriptor = -1;
    }
  }
}


/* connect to the alive socket */

void alive_connect()
{
  struct sockaddr_un sa;

  alive_descriptor = socket(PF_UNIX, SOCK_STREAM, 0);
  if (alive_descriptor < 0)
    handle_error("failed to make socket");

  sa.sun_family = AF_UNIX;
  strcpy(sa.sun_path, SOCKET_PATH);

  if (connect(alive_descriptor, (struct sockaddr *) &sa, sizeof(sa)) < 0)
  {
    close(alive_descriptor);
    alive_descriptor = -1;
    log("boot", "Failed to connect to angel, we are on our own from here.");
    return;
  }
  do_alive_ping();

  log("boot", "Alive and kicking");

}



/* work out whether a player is site banished or not */

int match_banish(player * p, char *line)
{
  char *addr;

  for (addr = p->num_addr; *addr; addr++, line++)
    if (*line == '*')
    {
      while (isdigit(*addr))
	addr++;
      line++;
    }
    else if (*addr != *line)
      return 0;

  return 1;
}

int do_banish(player * p)
{
  char *scan;
  int i;

  scan = banish_file.where;
  for (i = banish_file.length; i;)
  {
    if (*scan != '#' && match_banish(p, scan))
    {
      while (*scan != 'L' && *scan != 'C' && *scan != 'N' && *scan != '\n')
	scan++;
      switch (*scan)
      {
	case 'C':
	  return 1;
	case 'N':
	  p->flags |= CLOSED_TO_NEWBIES;
	  break;
	case 'L':
	  p->flags |= SITE_LOG;
	  break;
      }
      return 0;
    }
    while (i && *scan != '\n')
    {
      scan++;
      i--;
    }
    if (i)
    {
      scan++;
      i--;
    }
  }
  return 0;
}

/* Newbie spam patch by Phypor */

int no_more_newbies(player * p)
{
  int i = 0;
  player *scan;

  for (scan = flatlist_start; scan; scan = scan->flat_next)
    if (!strcmp(p->num_addr, scan->num_addr) &&
	!(scan->residency & BASE))
      i++;

  return (i > atoi(get_config_msg("site_newbies"))) ? 1 : 0;
}

/* accept new connection on the main socket */

void accept_new_connection(void)
{
  struct sockaddr_in incoming;
  struct hostent *hp;
#ifdef HAVE_SOCKLEN_T
  socklen_t length;
#else
  int length;
#endif /* HAVE_SOCKLEN_T */
  int new_socket;
  player *p;
  int dummy = 1, no1, no2, no3, no4;
#ifdef LOLIGO
  char *numerical_address;
#endif /* LOLIGO */

  length = sizeof(incoming);
  new_socket = accept(main_descriptor, (struct sockaddr *) &incoming, &length);
  if ((new_socket < 0) && errno == EINTR)
  {
    log("error", "EINTR accept trap");
    return;
  }
  if (new_socket < 0)
  {
    log("error", "Error accepting new connection.");
    return;
  }

  if (ioctl(new_socket, FIONBIO, &dummy) < 0)
    handle_error("Can't set non-blocking");

  if (current_players >= max_players)
  {
    write(new_socket, full_msg.where, full_msg.length);
    out_current += full_msg.length;
    out_pack_current++;
    return;
  }
  p = create_player();
  current_player = p;
  p->fd = new_socket;

  strncpy(p->num_addr, inet_ntoa(incoming.sin_addr), MAX_INET_ADDR - 2);
#ifdef LOLIGO
  numerical_address = p->num_addr;
#else
  hp = gethostbyaddr((char *) &(incoming.sin_addr.s_addr),
		     sizeof(incoming.sin_addr.s_addr),
		     AF_INET);
  if (hp)
    strncpy(p->inet_addr, hp->h_name, MAX_INET_ADDR - 2);
  else
    strncpy(p->inet_addr, p->num_addr, MAX_INET_ADDR - 2);
#endif
  sscanf(p->num_addr, "%d.%d.%d.%d", &no1, &no2, &no3, &no4);

  /* This is to stop a well known individual spamming talkers with newbies
     (code by Phypor) */

  if (no_more_newbies(p))
  {
    tell_player(p, "\n\n"
		LINE
		"Sorry but there are too many newbies on at the moment. Please wait a couple\n"
		"of minutes and try again ...\n\n"
		LINE
		"\n\n\n");
    destroy_player(p);
    return;
  }
  if (do_banish(p))
  {
    write(new_socket, banish_msg.where, banish_msg.length);
    out_current += banish_msg.length;
    out_pack_current++;
    destroy_player(p);
    return;
  }
  if (time(0) < splat_timeout && no1 == splat1 && no2 == splat2)
  {
    write(new_socket, splat_msg.where, splat_msg.length);
    out_current += banish_msg.length;
    out_pack_current++;
    destroy_player(p);
    return;
  }
  else
  {
#ifdef IDENT
    send_ident_request(p, &incoming);
#endif
    connect_to_prog(p);
  }
  current_player = 0;
}

/* turn on and off echo for passwords */

void password_mode_on(player * p)
{
  p->flags |= PASSWORD_MODE;
  p->mode |= PASSWORD;
  if (!(p->flags & DO_LOCAL_ECHO))
    tell_player(p, "\377\373\001");
}

void password_mode_off(player * p)
{
  p->flags &= ~PASSWORD_MODE;
  p->mode &= ~PASSWORD;
  if (!(p->flags & DO_LOCAL_ECHO))
    tell_player(p, "\377\374\001");
}

/* do a backspace */

void backspace(player * p)
{
  p->ibuffer[p->ibuff_pointer] = 0;
  if (p->ibuff_pointer > 0)
    p->ibuff_pointer--;
}

/* handle telnet control codes */

void telnet_options(player * p)
{
  unsigned char c;

  if (read(p->fd, &c, 1) != 1)
    return;
  switch (c)
  {
    case WONT:
      if (read(p->fd, &c, 1) != 1)
	return;
      switch (c)
      {
	case TELOPT_STATUS:
	  ping_respond(p);
	  break;
      }
      break;
    case EC:
      backspace(p);
      break;
    case EL:
      p->ibuff_pointer = 0;
      break;
    case IP:
      quit(p, 0);
      break;
    case DO:
      if (read(p->fd, &c, 1) != 1)
	return;
      switch (c)
      {
	case TELOPT_ECHO:
	  if (!(p->flags & PASSWORD_MODE))
	    p->flags |= DO_LOCAL_ECHO;	/* start local echo */
	  break;
	case TELOPT_SGA:
	  break;
	case TELOPT_EOR:
	  p->flags |= EOR_ON;
	  p->flags &= ~IAC_GA_DO;
	  tell_player(p, (char [4]) {IAC, WILL, TELOPT_EOR, '\0'});
	  break;
      }
      break;
    case DONT:
      if (read(p->fd, &c, 1) != 1)
	return;
      switch (c)
      {
	case TELOPT_ECHO:
	  p->flags &= ~DO_LOCAL_ECHO;	/* stop local echo */
	  break;
	case TELOPT_SGA:
	  break;
	case TELOPT_EOR:
	  p->flags &= ~EOR_ON;
	  if (p->system_flags & IAC_GA_ON)
	    p->flags |= IAC_GA_DO;
	  break;
      }
      break;
  }
}


/* gets any input from one player */

void get_player_input(player * p)
{
  int chars_ready = 0;
  char *oldstack, c;

  oldstack = stack;

  if (ioctl(p->fd, FIONREAD, &chars_ready) == -1)
  {
    quit(p, 0);
    log("error", "PANIC on FIONREAD ioctl");
    perror("SpoooN (socket.c)");
    return;
  }
  if (!chars_ready)
  {
    if (sys_flags & VERBOSE)
    {
      if (p->lower_name[0])
      {
	sprintf(oldstack, "%s went netdead.", p->name);
	stack = end_string(oldstack);
	log("connection", oldstack);
      }
      else
	log("connection", "Connection went netdead on login.");
    }
    quit(p, 0);
    stack = oldstack;
    return;
  }
  in_current += chars_ready;
  in_pack_current++;

  stack = oldstack;

  for (; !(p->flags & PANIC) && chars_ready; chars_ready--)
    if (read(p->fd, &c, 1) != 1)
    {
      log("connection", "Caught read error on socket.");
      return;
    }
    else
      switch (c)
      {
	case -1:
	  p->flags &= ~(LAST_CHAR_WAS_R | LAST_CHAR_WAS_N);
	  telnet_options(p);
	  return;
	  break;
	case '\n':
	  if (!(p->flags & LAST_CHAR_WAS_R))
	  {
	    p->flags |= LAST_CHAR_WAS_N;
	    p->flags |= INPUT_READY;
	    p->ibuffer[p->ibuff_pointer] = 0;
	    p->ibuff_pointer = 0;
	    p->column = 0;
	    return;
	  }
	  break;
	case '\r':
	  if (!(p->flags & LAST_CHAR_WAS_N))
	  {
	    p->flags |= LAST_CHAR_WAS_R;
	    p->flags |= INPUT_READY;
	    p->ibuffer[p->ibuff_pointer] = 0;
	    p->ibuff_pointer = 0;
	    p->column = 0;
	    return;
	  }
	  break;
	default:
	  p->flags &= ~(LAST_CHAR_WAS_R | LAST_CHAR_WAS_N);
	  if (c == 8 || c == 127 || c == -9)
	  {
	    backspace(p);
	    break;
	  }
	  if ((c > 31) && (p->ibuff_pointer < (IBUFFER_LENGTH - 3)))
	  {
	    p->ibuffer[p->ibuff_pointer] = c;
	    p->ibuff_pointer++;
	    if ((!(p->flags & PASSWORD_MODE)) && (p->flags & DO_LOCAL_ECHO))
	    {
	      if (write(p->fd, &c, 1) < 0 && errno != EINTR)
	      {
		log("error", "Echoing back to player.");
		quit(p, 0);
		return;
	      }
	      out_current++;
	      out_pack_current++;
	    }
	  }
          else if (!(p->ibuff_pointer < (IBUFFER_LENGTH - 3)))
          {
            spam_warning(p);
            return;
	  }
	  break;
      }
}


/* this routine is called when idle */

void scan_sockets()
{
  fd_set fset;
  player *scan;
  struct timeval tv;

  FD_ZERO(&fset);

  FD_SET(main_descriptor, &fset);
  for (scan = flatlist_start; scan; scan = scan->flat_next)
  {
    if (!((scan->fd < 0) || (scan->flags & PANIC)))
      FD_SET(scan->fd, &fset);
  }

#ifdef IDENT
  if (IDENT_CLIENT_READ != -1)
    FD_SET(IDENT_CLIENT_READ, &fset);
#endif


#ifdef INTERCOM
  if (intercom_fd > -1)
  {
    if (intercom_last < time(NULL))
      kill_intercom();
    else
      FD_SET(intercom_fd, &fset);
  }
  else
    intercom_fd = establish_intercom_server();
#endif

  tv.tv_sec = 0;
  tv.tv_usec = 0;

  if (select(FD_SETSIZE, &fset, 0, 0, &tv) == -1)
    return;

  if (FD_ISSET(main_descriptor, &fset))
    accept_new_connection();

#ifdef IDENT
  if (IDENT_CLIENT_READ != -1)
    if (FD_ISSET(IDENT_CLIENT_READ, &fset))
      read_ident_reply();
#endif

  for (scan = flatlist_start; scan; scan = scan->flat_next)
    if (!(scan->fd <= 0 || scan->flags & (PANIC | INPUT_READY))
	&& FD_ISSET(scan->fd, &fset))
      get_player_input(scan);

#ifdef INTERCOM
  if (intercom_fd > -1 && FD_ISSET(intercom_fd, &fset))
    parse_incoming_intercom();
#endif

}

/* generic routine to write to one player */

void tell_player(player * p, char *str)
{
  file output;
  char *oldstack, *script;

  oldstack = stack;
  if (((p->fd) < 0) || (p->flags & PANIC) ||
      (!(p->location) && current_player != p))
  {
    return;
  }
  if (!(sys_flags & PANIC))
  {
    if (!test_receive(p))
      return;
  }
  str = masks_process(p, str);
  output = process_output(p, str);

  if (p->script)
  {
    script = stack;
    sprintf(stack, "emergency/%s.emergency", p->lower_name);
    stack = end_string(stack);
    lscripto(script, output.where, output.length);
    stack = script;
  }
  if (current_player != p && (command_type & PERSONAL || p->flags & TAGGED)
      && !(command_type & NO_HISTORY) && !((sys_flags & EVERYONE_TAG)
					   || (sys_flags & ROOM_TAG)))
    process_review(p, output.where, output.length);
  if (p->flags & SCRIPTING)
  {
    script = stack;
    sprintf(stack, "scripts/%s", p->script_file);
    stack = end_string(stack);
    lscripto(script, output.where, output.length);
    script = script;
  }
  if (!write_socket(&p->fd, output.where, output.length))
    quit(p, 0);
  out_current += output.length;
  out_pack_current++;
  stack = oldstack;
}

/* small derivative of tell player to save typing */

void tell_current(char *str)
{
  if (!current_player)
    return;
  tell_player(current_player, str);
}

/* non blockable raw tell */

void non_block_tell(player * p, char *str)
{
  file output;
  char *script, *oldstack;

  oldstack = stack;
  if (((p->fd) < 0) || (p->flags & PANIC))
    return;

  str = masks_process(p, str);
  output = process_output(p, str);

  if (p->script)
  {
    script = stack;
    sprintf(stack, "emergency/%s.emergency", p->lower_name);
    stack = end_string(stack);
    lscripto(script, output.where, output.length);
    stack = script;
  }
  if (!write_socket(&p->fd, output.where, output.length))
    quit(p, 0);
  out_current += output.length;
  out_pack_current++;
  stack = oldstack;
}


/* general routine to send a prompt */
/* modified so prompt is exactly what its supposed to be --Silver */

void do_prompt(player * p, char *str)
{
  char *oldstack = stack;

  sprintf(stack, "^N%s", str);
  stack = strchr(stack, 0);;
  if (p->flags & IAC_GA_DO)
  {
    *stack++ = (char) IAC;
    *stack++ = (char) GA;
  }
  if (p->flags & EOR_ON)
  {
    *stack++ = (char) IAC;
    *stack++ = (char) EOR;
  }
  *stack++ = 0;
  tell_player(p, oldstack);
  stack = oldstack;
}


void set_talker_addy(void)
{
  char buf[160], *ptr;
  struct hostent *he;

  gethostname(buf, 159);
  he = gethostbyname(buf);
  if (!he)
  {
    /* shit ass ouch! */
    LOGF("error", "gethostbyname fails, errno %d, %s\n",
	 errno, strerror(errno));
    strncpy(talker_alpha, "Host Name Error", 159);
    strncpy(talker_ip, "Host Name Error", 39);
    return;
  }
  if ((ptr = get_config_msg("site_alias")) && *ptr)
    strncpy(talker_alpha, ptr, 159);
  else
    strncpy(talker_alpha, he->h_name, 159);

  sprintf(talker_ip, "%d.%d.%d.%d",
	  (unsigned char) he->h_addr_list[0][0],
	  (unsigned char) he->h_addr_list[0][1],
	  (unsigned char) he->h_addr_list[0][2],
	  (unsigned char) he->h_addr_list[0][3]);
}

/* work out whether a player is site banished or not */

int is_site_banned(player * p, char *line, char *check)
{
  char *addr;

  for (addr = check; *addr; addr++, line++)
    if (*line == '*')
    {
      while (isdigit(*addr))
	addr++;
      line++;
    }
    else if (*addr != *line)
      return 0;
  return 1;
}

void check_banish_status(player * p, char *str)
{
  char *scan;
  int i;
  char Comments[300] = "", AdminBanning[30] = "";
  int Com = 0, AB = 0;
  int status = -1;



  if (!*str || !isdigit(*str))
  {
    tell_player(p, " Format: ckban <IP address>\n");
    return;
  }
  scan = banish_file.where;
  for (i = banish_file.length; i;)
  {
    if (*scan != '#' && is_site_banned(p, scan, str))
    {
      while (*scan != 'L' && *scan != 'C' && *scan != 'N' && *scan != '\n')
	scan++;
      switch (*scan)
      {
	case 'C':
	  status = 0;
	  break;
	case 'N':
	  status = 1;
	  break;
	case 'L':
	  status = 2;
	  break;
      }
    }
    if (status >= 0)
    {
      scan++;
      i--;
      while (i && isspace(*(scan)))
      {
	i--;
	scan++;
      }
      while (i && *scan != '(' && Com < 298)
      {
	Comments[Com++] = *scan++;
	i--;
      }
      Comments[Com++] = 0;
      if (i)
	scan++;
      while (i && *scan != ')' && AB < 28)
      {
	AdminBanning[AB++] = *scan++;
	i--;
      }
      AdminBanning[AB++] = 0;
      while (i && *scan != '\n')
      {
	scan++;
	i--;
      }
      if (i)
      {
	scan++;
	i--;
      }
      switch (status)
      {
	case 0:
	  TELLPLAYER(p, "Site is totally banned\nBanned by: %s\nReason: %s\n",
		     AdminBanning, Comments);
	  return;
	case 1:
	  TELLPLAYER(p, "Site is newbie banned\nBanned by: %s\nReason: %s\n",
		     AdminBanning, Comments);
	  return;
	case 2:
	  TELLPLAYER(p, "Site is logged, but not banned\nSited by: %s\nReason: %s\n",
		     AdminBanning, Comments);
	  return;
      }
    }
    else
    {
      while (i && *scan != '\n')
      {
	scan++;
	i--;
      }
      if (i)
      {
	scan++;
	i--;
      }
    }
  }
  tell_player(p, " That site appears to not be banned...\n");
  return;
}

void ping(player * p, char *str)
{
  char *oldstack = stack;

  memset(stack, 0, 10);
  *stack++ = (char) IAC;
  *stack++ = (char) DO;
  *stack++ = (char) TELOPT_STATUS;
  *stack++ = (char) NULL;

  write(p->fd, oldstack, strlen(oldstack));
  stack = oldstack;

  gettimeofday(&(p->ping_timer), (struct timezone *) NULL);

}

void ping_timed(player * p)
{
  p->last_ping = -1;		/* set the last ping to be pending */
  ping(p, "");
}

void ping_respond(player * p)
{
  struct timeval endtv;
  long pt;


  memset(&endtv, 0, sizeof(struct timeval));
  gettimeofday(&endtv, (struct timezone *) NULL);

  pt = ((endtv.tv_sec - p->ping_timer.tv_sec) * 1000000) +
    ((endtv.tv_usec - p->ping_timer.tv_usec));

  p->last_ping = pt;
  p->next_ping = PING_INTERVAL;

}

/* A better (imho) ping command than phypors -- Silver */
/* Note: This is a better command frontend not ping code!! */

void do_ping(player * p, char *str)
{
  char *oldstack, middle[80];
  player *scan;
  int page, pages, count;
  oldstack = stack;

  page = atoi(str);
  if (page <= 0)
    page = 1;
  page--;

  pages = (current_players - 1) / (TERM_LINES - 2);
  if (page > pages)
    page = pages;

  pstack_mid("Connection Speeds");

  count = page * (TERM_LINES - 2);
  for (scan = flatlist_start; count; scan = scan->flat_next)
    if (!scan)
    {
      tell_player(p, " Bad do_ping listing, abort.\n");
      log("error", "Bad do_ping list (socket.c)");
      stack = oldstack;
      return;
    }
    else if (scan->name[0])
      count--;
  for (count = 0; (count < (TERM_LINES - 1) && scan); scan = scan->flat_next)
    if (scan->name[0] && scan->location && !(scan->residency & ROBOT_PRIV))
    {
      sprintf(stack, "%-19s - %s (%ld.%02ld secs lag)", scan->name, ping_string(scan), scan->last_ping / 1000000, (scan->last_ping / 10000) % 1000000);
      stack = strchr(stack, 0);
      *stack++ = '\n';
      count++;
    }
  sprintf(middle, "Page %d of %d", page + 1, pages + 1);
  pstack_mid(middle);
  *stack++ = 0;
  tell_player(p, oldstack);
  stack = oldstack;
}

/*
   a new write_socket function to sort out nasty sigpipes
   (thanks to jamesa for this)
*/
 
ssize_t write_socket(int *fd, const void *where, size_t length)
{
 ssize_t output_length = 0;
    
 assert(length);
    
  if (!length)
    return (1);
    
 if (*fd != -1)
 {
   if ((output_length = write(*fd, where, length)) == -1)
     switch (errno)
     {
    case EINTR: /* interupt occured */
    case EAGAIN: /* too much data */
    case ENOSPC: /* too much data */
      return (output_length);

    case EPIPE: /* sigpipe */
    default:   
      close(*fd);
      *fd = -1;
      return (0);
     }
   else
     return (output_length);
 }
      
 return (0);
}