/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/interface.c,v 1.13 90/09/28 12:23:15 rearl Exp $ */

/*
 * $Log:	interface.c,v $
 * Revision 1.13  90/09/28  12:23:15  rearl
 * Fixed @boot bug!  Finally!
 * 
 * Revision 1.12  90/09/16  04:42:16  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.11  90/09/15  22:23:23  rearl
 * Fixed non-HOSTNAMES problem.  Well, problems.
 * 
 * Revision 1.10  90/09/13  06:25:47  rearl
 * Changed boot_off() a bit in hopes of finding the bug...
 * 
 * Revision 1.9  90/09/10  02:19:00  rearl
 * Changed NL line termination to CR/LF pairs.
 * 
 * Revision 1.8  90/09/04  18:43:55  rearl
 * Fixed bug with connecting players.
 * 
 * Revision 1.7  90/08/27  03:27:03  rearl
 * Fixed dump_status logging.
 * 
 * Revision 1.6  90/08/15  03:02:23  rearl
 * Took out #ifdef CONNECT_MESSAGES.
 * 
 * Revision 1.5  90/08/02  18:46:38  rearl
 * Fixed calls to log functions.
 * 
 * Revision 1.4  90/07/29  17:35:28  rearl
 * Some minor fix-ups, most notably, gives number of players connected
 * in the WHO list (dump_users()).
 * 
 * Revision 1.3  90/07/23  14:52:33  casie
 * *** empty log message ***
 * 
 * Revision 1.2  90/07/21  01:40:25  casie
 * corrected a bug with the log_status function call.
 * 
 * Revision 1.1  90/07/19  23:03:42  casie
 * Initial revision
 * 
 *
 */

#include "copyright.h"
#include "config.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/errno.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include "db.h"
#include "interface.h"
#include "params.h"

int	shutdown_flag = 0;

static const char *connect_fail = "Either that player does not exist, or has a different password.\r\n";
#ifndef REGISTRATION
static const char *create_fail = "Either there is already a player with that name, or that name is illegal.\r\n";
#endif /* REGISTRATION */
static const char *flushed_message = "<Output Flushed>\r\n";
static const char *shutdown_message = "\r\n\r\n *** Shutdown now!  Back to RL! ***\r\n\r\n";

static int sock;
static int ndescriptors = 0;

struct descriptor_data *descriptor_list = 0;

void process_commands();
void shovechars(int);
void shutdownsock(struct descriptor_data *);
struct descriptor_data * initializesock(int, const char *);
void make_nonblocking(int);
void freeqs(struct descriptor_data *);
void welcome_user(struct descriptor_data *);
void check_connect(struct descriptor_data *, const char *);
void close_sockets();
int boot_off(dbref);
const char * addrout (long);
void dump_users(struct descriptor_data *, char *);
void set_signals(void);
struct descriptor_data *new_connection(int);
void parse_connect (const char *, char *, char *, char *);
void set_userstring (char **, const char *);
int do_command (struct descriptor_data *, char *);
char * strsave (const char *);
int make_socket(int);
int queue_string(struct descriptor_data *, const char *);
int queue_write(struct descriptor_data *, const char *, int);
int process_output(struct descriptor_data *);
int process_input(struct descriptor_data *);
int bailout(int);
void make_sigio();
void announce_connect(dbref);
void announce_disconnect(dbref);
char * time_format_1(long);
char * time_format_2(long);
void inc_gotio();

#define MALLOC(result, type, number) do {			\
    if (!((result) = (type *) malloc ((number) * sizeof (type))))	\
        panic("Out of memory");				\
    } while (0)

#define FREE(x) (free((void *) x))

int check_awake(dbref player)
{
  struct descriptor_data *d;
  int retval = 0;
  for(d = descriptor_list; d; d = d->next)
  {
    if (d->connected && d->player == player)
      retval++;
  }
  return retval;
}

#ifndef BOOLEXP_DEBUGGING
void main(int argc, char **argv)
{
  if (argc < 3) {
    fprintf (stderr, "Usage: %s infile dumpfile [port]\n", *argv);
    exit (1);
  }
  log_status("INIT: TinyMAGE %s starting.\n", "version");
  sock=make_socket(argc>=4?atoi(argv[3]):TINYPORT);
  if (init_game (argv[1], argv[2]) < 0) {
    fprintf (stderr, "Couldn't load %s!\n", argv[1]);
    exit (2);
  }

  startup_time = time(NULL);

  set_signals ();
#ifdef MUD_ID
  do_setuid();
#endif /* MUD_ID */
  /* go do it */
  shovechars (argc >= 4 ? atoi (argv[3]) : TINYPORT);
  close_sockets ();
  dump_database ();
  exit (0);
}
#endif /*BOOLEXP_DEBUGGING*/

void set_signals(void)
{
  int dump_status(void);
  
  /* we don't care about SIGPIPE, we notice it in select() and write() */
  signal (SIGPIPE, SIG_IGN);
  
  /* standard termination signals */
  signal (SIGINT, (void *) bailout);
  signal (SIGTERM, (void *) bailout);

  /* catch these because we might as well */
  signal (SIGQUIT, (void *) bailout);
  signal (SIGTRAP, (void *) bailout);
  signal (SIGIOT, (void *) bailout);
  signal (SIGEMT, (void *) bailout);
  signal (SIGFPE, (void *) bailout);
  signal (SIGBUS, (void *) bailout);
  signal (SIGSEGV, (void *) bailout);
  signal (SIGSYS, (void *) bailout);
  signal (SIGTERM, (void *) bailout);
  signal (SIGXCPU, (void *) bailout);
  signal (SIGXFSZ, (void *) bailout);
  signal (SIGVTALRM, (void *) bailout);
  signal (SIGUSR2, (void *) bailout);
  
  /* status dumper (predates "WHO" command) */
  signal (SIGUSR1, (void *) dump_status);

  /* Okay, now set up the sigo thing to increment the GOTIO var.. */
  {
    signal(SIGIO, (void *) inc_gotio);
  }
}

void inc_gotio()
{
  gotio = 1;
}

int notify_internal(dbref player, const char *msg)
{
  struct descriptor_data *d;
  int retval = 0;
  
/*  msg = get_uncompress(msg); */
  
  for(d = descriptor_list; d; d = d->next) {
    if (d->connected && d->player == player) {
      if(msg) {
	queue_string(d, msg);
	queue_write(d, "\r\n", 2);
      }
      retval++;
    }
  }
  return retval;
}

struct timeval timeval_sub(struct timeval now, struct timeval then)
{
  now.tv_sec -= then.tv_sec;
  now.tv_usec -= then.tv_usec;
  if (now.tv_usec < 0) {
    now.tv_usec += 1000000;
    now.tv_sec--;
  }
  return now;
}

int msec_diff(struct timeval now, struct timeval then)
{
  return ((now.tv_sec - then.tv_sec) * 1000
	  + (now.tv_usec - then.tv_usec) / 1000);
}

struct timeval msec_add(struct timeval t, int x)
{
  t.tv_sec += x / 1000;
  t.tv_usec += (x % 1000) * 1000;
  if (t.tv_usec >= 1000000) {
    t.tv_sec += t.tv_usec / 1000000;
    t.tv_usec = t.tv_usec % 1000000;
  }
  return t;
}

struct timeval update_quotas(struct timeval last, struct timeval current)
{
  int nslices;
  int cmds_per_time;
  struct descriptor_data *d;
  
  nslices = msec_diff (current, last) / COMMAND_TIME_MSEC;
  
  if (nslices > 0) {
    for (d = descriptor_list; d; d = d -> next) {
      if (d -> connected) {
	cmds_per_time = ((FLAGS(d->player) & INTERACTIVE)
			 ? (COMMANDS_PER_TIME * 6) : COMMANDS_PER_TIME);
      } else {
	cmds_per_time = COMMANDS_PER_TIME;
      }
      d -> quota += cmds_per_time * nslices;
      if (d -> quota > COMMAND_BURST_SIZE)
	d -> quota = COMMAND_BURST_SIZE;
    }
  }
  return msec_add (last, nslices * COMMAND_TIME_MSEC);
}

  int maxd;
  struct descriptor_data *newd;
  fd_set input_set, output_set;
void shovechars(int port)
{
  long now;
  struct timeval last_slice, current_time;
  struct timeval next_slice;
  struct timeval timeout, slice_timeout;
  struct descriptor_data *d, *dnext;
  int avail_descriptors;
  
  maxd = sock+1;
  gettimeofday(&last_slice, (struct timezone *) 0);
  
  avail_descriptors = getdtablesize() - 4;
  
  while (shutdown_flag == 0) {
    gettimeofday(&current_time, (struct timezone *) 0);
    last_slice = update_quotas (last_slice, current_time);
    
    process_commands();
    
    if (shutdown_flag)
      break;
    timeout.tv_sec = que_check()?0:1;		/* changed from 1000 for objects. */
    timeout.tv_usec = que_check()?10000:0;
    next_slice = msec_add (last_slice, COMMAND_TIME_MSEC);
    slice_timeout = timeval_sub (next_slice, current_time);
    
    FD_ZERO (&input_set);
    FD_ZERO (&output_set);
    if (ndescriptors < avail_descriptors)
      FD_SET (sock, &input_set);
    for (d = descriptor_list; d; d=d->next) {
      if (d->input.head)
	timeout = slice_timeout;
      else
	FD_SET (d->descriptor, &input_set);
      if (d->output.head)
	FD_SET (d->descriptor, &output_set);
    }
    
    if (select (maxd, &input_set, &output_set,
		(fd_set *) 0, &timeout) < 0) {
      if (errno != EINTR) {
	perror ("select");
	return;
      }
    } else {
      (void) time ((time_t *)&now);
      if (FD_ISSET (sock, &input_set)) {
	if (!(newd = new_connection (sock))) {
	  if (errno
	      && errno != EINTR
	      && errno != EMFILE
	      && errno != ENFILE) {
	    perror ("new_connection");
	    return;
	  }
	} else {
	  if (newd->descriptor >= maxd)
	    maxd = newd->descriptor + 1;
	}
      }
      for (d = descriptor_list; d; d = dnext) {
	dnext = d->next;
	if (FD_ISSET (d->descriptor, &input_set)) {
	  d->last_time = now;
	  if (!process_input (d)) {
	    shutdownsock (d);
	    continue;
	  }
	}
	if (FD_ISSET (d->descriptor, &output_set)) {
	  if (!process_output (d)) {
	    shutdownsock (d);
	  }
	}
      }
    }
  }
}

struct descriptor_data *new_connection(int sock)
{
  int newsock;
  struct sockaddr_in addr;
  int addr_len;
  char hostname[128];
  
  addr_len = sizeof (addr);
  newsock = accept (sock, (struct sockaddr *) & addr, &addr_len);
  if (newsock < 0) {
    return 0;
#ifdef LOCKOUT
  } else if(forbidden_site(ntohl(addr.sin_addr.s_addr))) {
    log_status("REFUSED: connection from %s(%d) on descriptor %d\n",
	       addrout(ntohl (addr.sin_addr.s_addr)),
	       ntohs(addr.sin_port), newsock);
    shutdown(newsock, 2);
    close(newsock);
    errno = 0;
    return 0;
#endif /* LOCKOUT */
  } else {
    strcpy (hostname, addrout (addr.sin_addr.s_addr));
    log_status("ACCEPT: %s(%d) on descriptor %d\n", hostname,
	       ntohs (addr.sin_port), newsock);
    return initializesock (newsock, hostname);
  }
}

const char *addrout(long a)
{
  static char buf[32];
#ifdef HOSTNAMES
  struct hostent *he;

  he = gethostbyaddr(&a, sizeof(a), AF_INET);
  if (he)
    return he->h_name;
  else
#endif /* HOSTNAMES */
  a = ntohl(a);
  sprintf (buf, "%d.%d.%d.%d", (a >> 24) & 0xff, (a >> 16) & 0xff,
	   (a >> 8) & 0xff, a & 0xff);
  return buf;
}

void clearstrings(struct descriptor_data *d)
{
  if (d->output_prefix) {
    FREE(d->output_prefix);
    d->output_prefix = 0;
  }
  if (d->output_suffix) {
    FREE(d->output_suffix);
    d->output_suffix = 0;
  }
}

void shutdownsock(struct descriptor_data *d)
{
  if (d->connected) {
    log_status ("DISCONNECT: descriptor %d player %s(%d)\n",
		d->descriptor, NAME(d->player), d->player);
    announce_disconnect (d->player);
  } else {
    log_status ("DISCONNECT: descriptor %d never connected.\n",
		d->descriptor);
  }
  clearstrings (d);
  shutdown (d->descriptor, 2);
  close (d->descriptor);
  freeqs (d);
  *d->prev = d->next;
  if (d->next)
    d->next->prev = d->prev;
  FREE (d);
  ndescriptors--;
}

struct descriptor_data *initializesock(int s, const char *hostname)
{
  struct descriptor_data *d;

  ndescriptors++;
  MALLOC(d, struct descriptor_data, 1);
  d->descriptor = s;
  d->connected = 0;
  make_nonblocking (s);
  make_sigio (s);
  d->output_prefix = 0;
  d->output_suffix = 0;
  d->output_size = 0;
  d->output.head = 0;
  d->output.tail = &d->output.head;
  d->input.head = 0;
  d->input.tail = &d->input.head;
  d->raw_input = 0;
  d->raw_input_at = 0;
  d->quota = COMMAND_BURST_SIZE;
  d->last_time = 0;
  d->hostname = alloc_string(hostname);
  if (descriptor_list)
    descriptor_list->prev = &d->next;
  d->next = descriptor_list;
  d->prev = &descriptor_list;
  descriptor_list = d;
  d->connected_at=time(NULL);
  welcome_user (d);
  return d;
}

int make_socket(int port)
{
  int s;
  struct sockaddr_in server;
  int opt;
  
  s = socket (AF_INET, SOCK_STREAM, 0);
  if (s < 0) {
    perror ("creating stream socket");
    exit (3);
  }
  opt = 1;
  if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
		  (char *) &opt, sizeof (opt)) < 0) {
    perror ("setsockopt");
    exit (1);
  }
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons (port);
  if (bind (s, (struct sockaddr *) & server, sizeof (server))) {
    perror ("binding stream socket");
    close (s);
    exit (4);
  }
  listen (s, 5);
  return s;
}

struct text_block *make_text_block(const char *s, int n)
{
  struct text_block *p;
  
  MALLOC(p, struct text_block, 1);
  MALLOC(p->buf, char, n);
  bcopy (s, p->buf, n);
  p->nchars = n;
  p->start = p->buf;
  p->nxt = 0;
  return p;
}

void free_text_block (struct text_block *t)
{
  FREE (t->buf);
  FREE ((char *) t);
}

void add_to_queue(struct text_queue *q, const char *b, int n)
{
  struct text_block *p;
  
  if (n == 0) return;
  
  p = make_text_block (b, n);
  p->nxt = 0;
  *q->tail = p;
  q->tail = &p->nxt;
}

int flush_queue(struct text_queue *q, int n)
{
  struct text_block *p;
  int really_flushed = 0;
  
  n += strlen(flushed_message);
  
  while (n > 0 && (p = q->head)) {
    n -= p->nchars;
    really_flushed += p->nchars;
    q->head = p->nxt;
    free_text_block (p);
  }
  p = make_text_block(flushed_message, strlen(flushed_message));
  p->nxt = q->head;
  q->head = p;
  if (!p->nxt)
    q->tail = &p->nxt;
  really_flushed -= p->nchars;
  return really_flushed;
}

int queue_write(struct descriptor_data *d, const char *b, int n)
{
  int space;
  
  space = MAX_OUTPUT - d->output_size - n;
  if (space < 0)
    d->output_size -= flush_queue(&d->output, -space);
  add_to_queue (&d->output, b, n);
  d->output_size += n;
  return n;
}

int queue_string(struct descriptor_data *d, const char *s)
{
  return queue_write (d, s, strlen (s));
}

int process_output(struct descriptor_data *d)
{
  struct text_block **qp, *cur;
  int cnt;
  
  for (qp = &d->output.head; cur = *qp;) {
    cnt = write (d->descriptor, cur -> start, cur -> nchars);
    if (cnt < 0) {
      if (errno == EWOULDBLOCK)
	return 1;
      return 0;
    }
    d->output_size -= cnt;
    if (cnt == cur -> nchars) {
      if (!cur -> nxt)
	d->output.tail = qp;
      *qp = cur -> nxt;
      free_text_block (cur);
      continue;		/* do not adv ptr */
    }
    cur -> nchars -= cnt;
    cur -> start += cnt;
    break;
  }
  return 1;
}

void make_nonblocking(int s)
{
  if (fcntl (s, F_SETFL, FNDELAY) == -1) {
    perror ("make_nonblocking: fcntl");
    panic ("FNDELAY fcntl failed");
  }
}
void make_sigio(int s)
{
  if(fcntl(s,F_SETFL,FASYNC) == -1) {
    perror("make_sigo: fcntl");
    panic("FASYNC fcntl failed.");
  }
}

void freeqs(struct descriptor_data *d)
{
  struct text_block *cur, *next;
  
  cur = d->output.head;
  while (cur) {
    next = cur -> nxt;
    free_text_block (cur);
    cur = next;
  }
  d->output.head = 0;
  d->output.tail = &d->output.head;
  
  cur = d->input.head;
  while (cur) {
    next = cur -> nxt;
    free_text_block (cur);
    cur = next;
  }
  d->input.head = 0;
  d->input.tail = &d->input.head;
  
  if (d->raw_input)
    FREE (d->raw_input);
  d->raw_input = 0;
  d->raw_input_at = 0;
}

void goodbye_user(struct descriptor_data *d)
{
  write (d->descriptor, LEAVE_MESSAGE, strlen (LEAVE_MESSAGE));
}

char *strsave (const char *s)
{
  char *p;
  
  MALLOC (p, char, strlen(s) + 1);
  
  if (p)
    strcpy (p, s);
  return p;
}

void save_command (struct descriptor_data *d, const char *command)
{
  add_to_queue (&d->input, command, strlen(command)+1);
}

int process_input (struct descriptor_data *d)
{
  char buf[1024];
  int got;
  char *p, *pend, *q, *qend;
  
  got = read (d->descriptor, buf, sizeof buf);
  if (got <= 0)
    return 0;
  if (!d->raw_input) {
    MALLOC(d->raw_input,char,MAX_COMMAND_LEN);
    d->raw_input_at = d->raw_input;
  }
  p = d->raw_input_at;
  pend = d->raw_input + MAX_COMMAND_LEN - 1;
  for (q=buf, qend = buf + got; q < qend; q++) {
    if (*q == '\n') {
      *p = '\0';
      if (p > d->raw_input)
	save_command (d, d->raw_input);
      p = d->raw_input;
    } else if (p < pend && isascii (*q) && isprint (*q)) {
      *p++ = *q;
    }
  }
  if(p > d->raw_input) {
    d->raw_input_at = p;
  } else {
    FREE(d->raw_input);
    d->raw_input = 0;
    d->raw_input_at = 0;
  }
  return 1;
}

void set_userstring (char **userstring, const char *command)
{
  if (*userstring) {
    FREE(*userstring);
    *userstring = 0;
  }
  while (*command && isascii (*command) && isspace (*command))
    command++;
  if (*command)
    *userstring = strsave (command);
}

void process_commands(void)
{
  int nprocessed;
  struct descriptor_data *d;
  struct text_block *t;
  
  do {
    nprocessed = 0;
    for (d = descriptor_list; d; d = d->next) {
      if (d -> quota > 0 && (t = d -> input.head)) {
	d -> quota--;
	nprocessed++;
	if (!do_command (d, t -> start)) {
	  shutdownsock (d);
	} else {
	  d -> input.head = t -> nxt;
	  if (!d -> input.head)
	    d -> input.tail = &d -> input.head;
	  free_text_block (t);
	}
      }
    }
  } while (nprocessed > 0);
  check_waits();		/* check the wait things. */
}

int do_command (struct descriptor_data *d, char *command)
{
  if (!strcmp (command, QUIT_COMMAND)) {
    goodbye_user (d);
    return 0;
  } else if (!strncmp (command, WHO_COMMAND, sizeof(WHO_COMMAND) - 1)) {
    if (d->output_prefix) {
      queue_string (d, d->output_prefix);
      queue_write (d, "\r\n", 2);
    }
    dump_users (d, command + sizeof(WHO_COMMAND) - 1);
    if (d->output_suffix) {
      queue_string (d, d->output_suffix);
      queue_write (d, "\r\n", 2);
    }
  } else if (!strncmp (command, PREFIX_COMMAND, sizeof (PREFIX_COMMAND) - 1)) {
    set_userstring (&d->output_prefix, command+sizeof(PREFIX_COMMAND) - 1);
  } else if (!strncmp (command, SUFFIX_COMMAND, sizeof (SUFFIX_COMMAND) - 1)) {
    set_userstring (&d->output_suffix, command+sizeof(SUFFIX_COMMAND) - 1);
  } else {
    if (d->connected) {
      if (d->output_prefix) {
	queue_string (d, d->output_prefix);
	queue_write (d, "\r\n", 2);
      }
      if(!string_compare(command, BREAK_COMMAND)) {
	FLAGS(d->player) |= HALTED;
	if(FLAGS(d->player)&INTERACTIVE)
	  process_command (d->player, command, d->player, 0);
      }
      else if(FLAGS(d->player)&INTERACTIVE) {
	process_command (d->player, command, d->player, 0);
      }
      else if(!do_act(d->player, command, d->player)) {
	process_command (d->player, command, d->player, 0);
      }
      if (d->output_suffix) {
	queue_string (d, d->output_suffix);
	queue_write (d, "\r\n", 2);
      }
    } else {
      check_connect (d, command);
    }
  }
  return 1;
}

void interact_warn (dbref player)
{
    char buf[BUFSIZ];
    sprintf(buf, "***  %s  ***",
	    DBFETCH(player)->run ?
            "You are currently using a program.  Use \"@Q\" to return to a more reasonable state of control." :
	    (DBFETCH(player)->insert_mode ?
	     "You are currently inserting MUF program text.  Use \".\" to return to the editor, then \"quit\" if you wish to return to your regularly scheduled Muck universe." :
	     "You are currently using the MUF program editor."));
    notify(player, buf);
}

void check_connect (struct descriptor_data *d, const char *msg)
{
  char command[MAX_COMMAND_LEN];
  char user[MAX_COMMAND_LEN];
  char password[MAX_COMMAND_LEN];
  dbref player;
  
  parse_connect (msg, command, user, password);
  
  if (!strncmp (command, "co", 2)) {
    FILE *f;
    player = connect_player (user, password);
    if(f=fopen("data/nologin.txt","r")) {
      char buf[1024];
      while(fgets(buf,sizeof(buf),f)) {
	queue_string(d,buf);
      }
      fclose(f);
      if(!Wizard(player))
	player=NOTHING;
      }
    else
      if (player == NOTHING) {
	queue_string (d, connect_fail);
	log_status ("FAILED CONNECT %s on descriptor %d\n",
		  user, d->descriptor);
      }
    if(player != NOTHING) {
      log_status ("CONNECTED: %s(%d) on descriptor %d\n",
		  NAME(player), player, d->descriptor);
      d->connected = 1;
      d->connected_at = time(NULL);
      d->player = player;
      announce_connect (player);
      do_look_around (player);
      if(FLAGS(player)&INTERACTIVE) {
	interact_warn (player);
      }
      else {
	if(get_attr(GLOBAL_ENVIRONMENT, "Aconnect") && !Wizard(player))
	  trigobj(player, get_attr(GLOBAL_ENVIRONMENT, "Aconnect"), player);
	if(get_attr(player, "Aconnect"))
	  trigobj(player, get_attr(player, "Aconnect"), player);
      }
    }
  } else if (!strncmp (command, "cr", 2)) {
#ifndef REGISTRATION
    player = create_player (user, password);
    if (player == NOTHING) {
      queue_string (d, create_fail);
      log_status ("FAILED CREATE %s on descriptor %d\n",
                  user, d->descriptor);
    } else {
      log_status ("CREATED %s(%d) on descriptor %d\n",
                  NAME(player), player, d->descriptor);
      d->connected = 1;
      d->connected_at = time(NULL);
      d->player = player;
      announce_connect (player);
      do_look_around (player);
    }
#else /* REGISTRATION */
    queue_string (d, REG_MSG);
    log_status ("FAILED CREATE %s on descriptor %d\n",
		user, d->descriptor);
#endif /* REGISTRATION */
  } else {
    welcome_user (d);
  }
}

void parse_connect (const char *msg, char *command, char *user, char *pass)
{
  char *p;
  
  while (*msg && isascii(*msg) && isspace (*msg))
    msg++;
  p = command;
  while (*msg && isascii(*msg) && !isspace (*msg))
    *p++ = *msg++;
  *p = '\0';
  while (*msg && isascii(*msg) && isspace (*msg))
    msg++;
  p = user;
  while (*msg && isascii(*msg) && !isspace (*msg))
    *p++ = *msg++;
  *p = '\0';
  while (*msg && isascii(*msg) && isspace (*msg))
    msg++;
  p = pass;
  while (*msg && isascii(*msg)) /* && isspace (*msg)) */
    *p++ = *msg++;
  *p = '\0';
}

int old_boot_off(dbref player)
{
  struct descriptor_data *d;
  struct descriptor_data *next;
  
  for (d = descriptor_list; d; d = next)
    {
      next = d->next;
      if (d->connected && d->player == player)
	{
	  process_output (d);
	  shutdownsock(d);
	  return 1;
	}
    }
  return 0;
}

int boot_loop(struct descriptor_data *d, dbref player)
{
  if(!d)
    return 0;
  if(boot_loop(d->next,player))
    return 1;
  if(d->connected && d->player == player)
    {
      process_output(d);
      shutdownsock(d);
      return 1;
    }
  return 0;
}
int dboot_loop(struct descriptor_data *d,int who)
{
if(!d)
return 0;
if(dboot_loop(d->next,who)) return 1;
if(d->connected && d->descriptor == who)
{ process_output(d); shutdownsock(d); return 1; }
return 0;
}
int dboot_off(int who) { return dboot_loop(descriptor_list,who);}
int boot_off(dbref player)
{
  return boot_loop(descriptor_list, player);
}

void close_sockets(void)
{
  struct descriptor_data *d, *dnext;
  
  for (d = descriptor_list; d; d = dnext) {
    dnext = d->next;
    write (d->descriptor, shutdown_message, strlen (shutdown_message));
    if (shutdown (d->descriptor, 2) < 0)
      perror ("shutdown");
    close (d->descriptor);
  }
  close (sock);
}

void emergency_shutdown(void)
{
  close_sockets();
}

int bailout(int sig)
{
  char message[1024];
  
  sprintf (message, "BAILOUT: caught signal %d", sig);
  panic(message);
  _exit (7);
  return 0;
}

int dump_status(void)
{
  struct descriptor_data *d;
  long now;
  char buf[BUFSIZ];
  
  (void) time ((time_t *)&now);
  log_status ("STATUS REPORT:\n");
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected) {
      sprintf(buf, "PLAYING descriptor %d player %s(%d) from host %s, %s.\n",
	      d->descriptor, NAME(d->player), d->player, d->hostname,
	      (d->last_time) ? "idle %d seconds" : "never used");
    } else {
      sprintf(buf, "CONNECTING descriptor %d from host %s, %s.\n",
	      d->descriptor, d->hostname,
	      (d->last_time) ? "idle %d seconds" : "never used");
    }
    log_status (buf, now - d->last_time);
  }
  return 0;
}

void dump_users(struct descriptor_data *e, char *user)
{
  struct descriptor_data *d;
  int wizard, players;
  long now;
  static char pbuf[BUFFER_LEN];

#ifdef GOD_MODE
  wizard = e->connected && God (e->player);
#else /* All wizards may see extended WHO */
  wizard = e->connected && Wizard (e->player);
#endif /* GOD_MODE */
/*
queue_string(e,"Sorry, who is broken. use small instead.\r\n");
return;*/
  puts("1.");
  while (*user && isspace(*user)) user++;
  if (!*user) user = NULL;
  
  (void) time ((time_t *)&now);
  queue_string (e, (wizard) ?
#ifdef HOSTNAMES  
		"FD Player Name               Location     On For Idle Host\r\n"
#else /* !HOSTNAMES */
		"FD Player Name                         Location     On For Idle Host\r\n"
#endif /* HOSTNAMES */
		: "Player Name          On For Idle\r\n");
  
  d = descriptor_list;
  players = 0;
  puts("2");
  while (d) {
    if (d->connected && ++players &&
	(!user || string_prefix(NAME(d->player), user))) {
      if (wizard) {
	puts("3");
#ifdef HOSTNAMES   /* Don't print flags to save space */
	  sprintf(pbuf, "%.*s(#%d)", PLAYER_NAME_LIMIT,
		  NAME(d->player), (int) d->player);
#else /* !HOSTNAMES */
	  strcpy(pbuf, unparse_object(e->player, d->player));
#endif /* HOSTNAMES */
	  sprintf(buf,"%-3d%-*s [%6d] %10s %4s %-.26s\r\n",
		  d->descriptor,
		  PLAYER_NAME_LIMIT +
#ifdef HOSTNAMES  /* Ack! */
		  9
#else  /* !HOSTNAMES */
		  19
#endif /* HOSTNAMES */
		  , pbuf,
		  (int) DBFETCH(d->player)->location,
		  time_format_1(now - d->connected_at),
		  d->last_time?time_format_2(now - d->last_time):"--",
		  d->hostname);
	} else {
	  sprintf(buf, "%-*s %10s %4s\r\n",
		  PLAYER_NAME_LIMIT,
		  NAME(d->player),
		  time_format_1(now - d->connected_at),
		  time_format_2(now - d->last_time));
	}
      queue_string (e, buf);
      puts("4");
      if(wizard && !d->connected) {
        puts("5");
        sprintf(buf,"%-3d%-*s [%6s] %10s %4s %s\r\n",
	        d->descriptor,
	        PLAYER_NAME_LIMIT +
#ifdef HOSTNAMES
	        9
#else
	        19
#endif
	        ,"","",
	        time_format_1(now-d->connected_at),
	        time_format_2(now-d->last_time),
	        d->hostname);
        queue_string(e,buf);
        puts("6");
      }
    }
    d = d->next;
    puts("7");
  }
  puts("8");
  sprintf(buf, "%d player%s %s connected.  Uptime: %s\r\n", players,
	  (players == 1) ? "" : "s", (players == 1) ? "is" : "are",
	  time_format_1(now - startup_time));
  /* add uptime to WHO */
  puts("9");
  queue_string (e, buf);
  puts("10");
}

char *time_format_1(long dt)
{
  register struct tm *delta;
  static char buf[BUFFER_LEN];
  
  delta = gmtime((time_t *)&dt);
  if (delta->tm_yday > 0)
    sprintf(buf, "%dd %02d:%02d",
	    delta->tm_yday, delta->tm_hour, delta->tm_min);
  else
    sprintf(buf, "%02d:%02d",
	    delta->tm_hour, delta->tm_min);
  return buf;
}

char *time_format_2(long dt)
{
  register struct tm *delta;
  static char buf[BUFFER_LEN];
  
  delta = gmtime((time_t *)&dt);

  if (delta->tm_yday > 0)
    sprintf(buf, "%dd", delta->tm_yday);
  else if (delta->tm_hour > 0)
    sprintf(buf, "%dh", delta->tm_hour);
  else if (delta->tm_min > 0)
    sprintf(buf, "%dm", delta->tm_min);
  else
    sprintf(buf, "%ds", delta->tm_sec);
  return buf;
}


void announce_connect(dbref player)
{
  dbref loc;
  char buf[BUFFER_LEN];
  
  global_trigger = player;
  
  if ((loc = getloc(player)) == NOTHING) return;
  if (Dark(player) || Dark(loc)) return;

  if(get_attr(player, "Oconnect"))
    sprintf(buf, "%s %s", NAME(player), get_attr(player, "Oconnect"));
  else
    sprintf(buf, "%s has connected.", NAME(player));

  notify_except(DBFETCH(loc)->contents, player, buf);
  notify_except(DBFETCH(player)->contents, player, buf);
}

void announce_disconnect(dbref player)
{
  dbref loc;
  char buf[BUFFER_LEN];
  global_trigger=player;
  if ((loc = getloc(player)) == NOTHING) return;
  if (Dark(player) || Dark(loc)) return;
  
  if(get_attr(player, "Odisconnect"))
    sprintf(buf, "%s %s", NAME(player), get_attr(player, "Odisconnect"));
  else
    sprintf(buf, "%s has disconnected.", NAME(player));
  
  notify_except(DBFETCH(loc)->contents, player, buf);
  notify_except(DBFETCH(player)->contents,player,buf);
}

#ifdef MUD_ID
void
  do_setuid(char *name)
{
#include <pwd.h>
  struct passwd *pw;
  
  if ((pw = getpwnam(name)) == NULL)
    {
      log_status("can't get pwent for %s\n", name);
      exit(1);
    }
  
  if (setuid(pw->pw_uid) == -1)
    {
      log_status("can't setuid(%d): ", pw->pw_uid);
      perror("setuid");
      exit(1);
    }
}
#endif /* MUD_ID */

void welcome_user(struct descriptor_data *d)
{
  
  FILE *f;
  char buf[BUFFER_LEN];
  
  if((f = fopen(WELC_FILE, "r")) == NULL)
    {
      queue_string (d, DEFAULT_WELCOME_MESSAGE);
      perror("spit_file: welcome.txt");
    }
  else
    {
      while(fgets(buf, sizeof buf, f)) 
	{
	  queue_string(d, buf);
	}
      fclose(f);
    }
}
void do_dboot(dbref who,char *what)
{
  if(!what || !*what) {
    notify(who, "Usage:  @dboot <descriptor #>");
    return;
  }
  if(!Wizard(who)) {
    notify(who,"Only a Wizard can boot someone off.");
    return;
  }
  dboot_off(atoi(what));

  sprintf(buf, "Descriptor %d booted.", atoi(what));
  notify(who, buf);
}

void db_loadsend(const char *msg)
{
  struct descriptor_data *d;
  if (FD_ISSET (sock, &input_set)) {
    if (!(newd = new_connection (sock))) {
      if (errno
	  && errno != EINTR
	  && errno != EMFILE
	  && errno != ENFILE) {
	perror ("new_connection");
	return;
      } else {
	if (newd->descriptor >= maxd)
	  maxd = newd->descriptor + 1;
      }
    }
  }
  for(d=descriptor_list;d;d=d->next) {
    queue_string(d,msg);
    queue_write(d,"\r\n",2);
  }
}