/* bsd.c */

#include "copyright.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.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 <string.h>
#include <sys/param.h>
#include "config.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "globals.h"

#ifdef MEM_CHECK
#include "mem_check.h"
#endif

extern dbref speaker;
extern int errno;
extern int reserved;
int shutdown_flag = 0;
extern dbref db_top;
int login_allow = 1;

char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN],
     cf_nologinmotd_msg[BUFFER_LEN];

#ifdef AT_DOING
static char poll[39];
#endif

static const char *connect_fail = "Either that player does not exist, or has a different password.\n";
#ifndef WCREAT
static const char *create_fail = "Either there is already a player with that name, or that name is illegal.\n";
#endif
static const char *flushed_message = "<Output Flushed>\n";
static const char *shutdown_message = "Going down - Bye\n";
static const char *asterisk_line =
    "*****************************************************************";

struct text_block {
  int nchars;
  struct text_block *nxt;
  char *start;
  char *buf;
};

struct text_queue {
  struct text_block *head;
  struct text_block **tail;
};

struct descriptor_data {
  int descriptor;
  int connected;
  char addr[51];
  dbref player;
  char *output_prefix;
  char *output_suffix;
  int output_size;
  struct text_queue output;
  struct text_queue input;
  char *raw_input;
  char *raw_input_at;
  long connected_at;
  long last_time;
  int quota;
#ifdef AT_DOING
  char doing[40];
#endif
  struct sockaddr_in address;	/* added 3/6/90 SCG */
  struct descriptor_data *next;
  struct descriptor_data **prev;
} *descriptor_list = 0;

static int sock;
static int ndescriptors = 0;
char ccom[BUFFER_LEN];
dbref cplr;

void process_commands();
void shovechars();
void shutdownsock();
struct descriptor_data *initializesock();
void make_nonblocking();
void freeqs();
void welcome_user();
void spew_message();
int check_connect();
void close_sockets();
#ifdef LOCKOUT
const char *addrout();
#endif
void dump_users();

#ifdef RWHO_SEND
#ifdef FULL_RWHO
void dump_rusers();
#endif
void rwho_update();
#endif

void set_signals();
struct descriptor_data *new_connection();
void parse_connect();
void set_userstring();
int do_command();
char *strsave();
int make_socket();
int queue_string();
int queue_write();
int process_output();
int process_input();
int bailout();
void srand();
void announce_connect();
void announce_disconnect();
const char *time_format_1();
const char *time_format_2();

#ifndef BOOLEXP_DEBUGGING
void main(argc, argv)
    int argc;
    char **argv;
{
  const char *def_db_in = DEF_DB_IN;
  const char *def_db_out = DEF_DB_OUT;
#ifdef AUTORESTART
  FILE *id;
#endif
  /* change default input database? */
  if (argc > 1) {
    --argc;
    def_db_in = *++argv;
  }
  /* change default dump database? */
  if (argc > 1) {
    --argc;
    def_db_out = *++argv;
  }

/*  this writes a file used by the restart script to check for active mush */
#ifdef AUTORESTART
  id = fopen("runid", "w");
  fprintf(id, "%d", getpid());
  fclose(id);
#endif

  srand(time(NULL));
  
  /* save a file descriptor */
  reserved = open("/dev/null", O_RDWR);

  if (init_game(def_db_in, def_db_out) < 0) {
    fprintf(stderr, "ERROR: Couldn't load %s! Exiting.\n", def_db_in);
    exit(2);
  }
  set_signals();
#ifdef RWHO_SEND
  rwhocli_setup(RWHOSERV, RWHOPASS, MUDNAME, VERSION);
#endif
  /* go do it */
  shovechars(argc > 1 ? atoi(*++argv) : TINYPORT);
  close_sockets();
  dump_database();
  close(sock);			/* patch moving this line here fixes
				 * @shutdown error */
  exit(0);
}
#endif				/* BOOLEXP_DEBUGGING */

void set_signals()
{
  /* 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);
}

void raw_notify(player, msg)
    dbref player;
    const char *msg;
{
  struct descriptor_data *d;
  if (!msg || *msg == '\0')
    return;
  if (!(db[player].flags & PLAYER_CONNECT) && login_allow)
    return;
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected && d->player == player) {
      queue_string(d, msg);
      queue_write(d, "\n", 1);
    }
  }
}

#define DESC struct descriptor_data
#define DESC_ITER_CONN(d) \
        for(d = descriptor_list;(d);d=(d)->next) \
	  if((d)->connected)

void raw_broadcast (inflags, template, a1, a2, a3, a4, a5, a6)
int inflags, a1, a2, a3, a4, a5, a6;
char *template;
{
  DESC *d;
  char tbuf1[BUFFER_LEN];

  if(!template || !*template) return;

  sprintf(tbuf1, template, a1, a2, a3, a4, a5, a6);

  DESC_ITER_CONN(d) {
    if((db[d->player].flags & inflags) == inflags) {
      queue_string(d, tbuf1);
      queue_write(d, "\n", 1);
      process_output(d);
    }
  }
}
 
struct timeval timeval_sub(now, then)
    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;
}

long msec_diff(now, then)
    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(t, x)
    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(last, current)
    struct timeval last;
    struct timeval current;
{
  int nslices;
  struct descriptor_data *d;
  nslices = (int) msec_diff(current, last) / COMMAND_TIME_MSEC;

  if (nslices > 0) {
    for (d = descriptor_list; d; d = d->next) {
      d->quota += COMMANDS_PER_TIME * nslices;
      if (d->quota > COMMAND_BURST_SIZE)
	d->quota = COMMAND_BURST_SIZE;
    }
  }
  return msec_add(last, nslices * COMMAND_TIME_MSEC);
}

void shovechars(port)
    int port;
{
  fd_set input_set, output_set;
  time_t now;
  struct timeval last_slice, current_time;
  struct timeval next_slice;
  struct timeval timeout, slice_timeout;
  int maxd, found;
  struct descriptor_data *d, *dnext;
  struct descriptor_data *newd;
  int avail_descriptors;
  extern void dispatch();

  sock = make_socket(port);
  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);

#ifdef IDLE_TIMEOUT
    /*check idle timeout */
    inactivity_check();
#endif

    process_commands();

    if (shutdown_flag)
      break;
    /* test for events */
    dispatch();
    /* any queued robot commands waiting? */
    timeout.tv_sec = test_top() ? 0 : 1000;
    timeout.tv_usec = 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 ((found = select(maxd, &input_set, &output_set,
			(fd_set *) 0, &timeout)) < 0) {
      if (errno != EINTR) {
	perror("select");
	return;
      }
    } else {
      /* if !found then time for robot commands */
      if (!found) {
	do_top();
	do_top();
	do_top();
	continue;
      }
      now = time((time_t *) 0);
      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(oldsock)
    int oldsock;
{
  int newsock;
  struct sockaddr_in addr;
  int addr_len;
  char tbuf1[BUFFER_LEN];

  addr_len = sizeof(addr);
  newsock = accept(oldsock, (struct sockaddr *) &addr, &addr_len);
  if (newsock < 0) {
    return 0;
#ifdef LOCKOUT
  } else if (forbidden_site(addrout(addr.sin_addr.s_addr))) {
    fprintf(stderr, "REFUSED CONNECTION from %s(%d) on descriptor %d\n",
	    addrout(addr.sin_addr.s_addr),
	    ntohs(addr.sin_port), newsock);
    shutdown(newsock, 2);
    close(newsock);
    errno = 0;
    return 0;
#endif				/* LOCKOUT */
  } else {
    time_t tt;
    strcpy (tbuf1, addrout(addr.sin_addr.s_addr));
    tt = time((time_t *) 0);
    fprintf(stderr, "USER CONNECT: des: %d host %s time: %s", newsock, tbuf1,
	    ctime(&tt));
    return initializesock(newsock, &addr, tbuf1);
  }
}

void clearstrings(d)
    struct descriptor_data *d;
{
  if (d->output_prefix) {
    free((void *) d->output_prefix);
#ifdef MEM_CHECK
    del_check("userstring");
#endif
    d->output_prefix = 0;
  }
  if (d->output_suffix) {
    free((void *)d->output_suffix);
#ifdef MEM_CHECK
    del_check("userstring");
#endif
    d->output_suffix = 0;
  }
}

void shutdownsock(d)
    struct descriptor_data *d;
{
  if (d->connected) {
    spew_message(d,LEAVE_MSG_FILE);
    fprintf(stderr, "DISCONNECT descriptor %d player %s(%d)\n",
	    d->descriptor, db[d->player].name, d->player);
    announce_disconnect(d->player);
  } else {
    fprintf(stderr, "DISCONNECT descriptor %d never connected\n",
	    d->descriptor);
  }
  process_output(d);
  clearstrings(d);
  shutdown(d->descriptor, 2);
  close(d->descriptor);
  freeqs(d);
  *d->prev = d->next;
  if (d->next)
    d->next->prev = d->prev;
  free((void *) d);
#ifdef MEM_CHECK
  del_check("descriptor");
#endif
  ndescriptors--;
}

struct descriptor_data *initializesock(s, a, addr)
    int s;
    struct sockaddr_in *a;
    char *addr;
{
  struct descriptor_data *d;
  ndescriptors++;
  d = (struct descriptor_data *) malloc(sizeof(struct descriptor_data));
  if(!d)
    panic("Out of memory.");
#ifdef MEM_CHECK
  add_check("descriptor");
#endif
  d->descriptor = s;
  d->connected = 0;
  make_nonblocking(s);
  d->output_prefix = 0;
  d->output_suffix = 0;
  d->output_size = 0;
  d->output.head = 0;
  d->player = 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;
#ifdef AT_DOING
  d->doing[0] = '\0';
#endif
  strncpy(d->addr, addr, 50);
  d->address = *a;		/* added 5/3/90 SCG */
  if (descriptor_list)
    descriptor_list->prev = &d->next;
  d->next = descriptor_list;
  d->prev = &descriptor_list;
  descriptor_list = d;
  welcome_user(d);
  return d;
}

int make_socket(port)
    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(s, n)
    const char *s;
    int n;
{
  struct text_block *p;
  p = (struct text_block *) malloc(sizeof(struct text_block));
  if(!p)
    panic("Out of memory");
  p->buf = (char *) malloc(sizeof(char) * n);
  if(!p->buf)
    panic("Out of memory");

#ifdef MEM_CHECK
  add_check("text_block");
  add_check("text_block_buff");
#endif
  bcopy(s, p->buf, n);
  p->nchars = n;
  p->start = p->buf;
  p->nxt = 0;
  return p;
}

void free_text_block(t)
    struct text_block *t;
{
   if (t) {
	if (t->buf)	
		free((void *) t->buf);
	free((void *) t);
    }
#ifdef MEM_CHECK
  del_check("text_block");
  del_check("text_block_buff");
#endif
}

void add_to_queue(q, b, n)
    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(q, n)
    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;
#ifdef DEBUG
    fprintf(stderr, "free_text_block(0x%x) at 1.\n", p);
#endif DEBUG
    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(d, b, n)
    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(d, s)
    struct descriptor_data *d;
    const char *s;
{
  return queue_write(d, s, strlen(s));
}

int process_output(d)
    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;
#ifdef DEBUG
      fprintf(stderr, "free_text_block(0x%x) at 2.\n", cur);
#endif DEBUG
      free_text_block(cur);
      continue;			/* do not adv ptr */
    }
    cur->nchars -= cnt;
    cur->start += cnt;
    break;
  }
  return 1;
}

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

void freeqs(d)
    struct descriptor_data *d;
{
  struct text_block *cur, *next;
  cur = d->output.head;
  while (cur) {
    next = cur->nxt;
#ifdef DEBUG
    fprintf(stderr, "free_text_block(0x%x) at 3.\n", cur);
#endif DEBUG
    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;
#ifdef DEBUG
    fprintf(stderr, "free_text_block(0x%x) at 4.\n", cur);
#endif DEBUG
    free_text_block(cur);
    cur = next;
  }
  d->input.head = 0;
  d->input.tail = &d->input.head;

  if (d->raw_input) {
    free((void *) d->raw_input);
#ifdef MEM_CHECK
    del_check("descriptor_raw_input");
#endif
  }
  d->raw_input = 0;
  d->raw_input_at = 0;
}

void welcome_user(d)
    struct descriptor_data *d;
{
  spew_message(d, WELCOME_MSG_FILE);
}

void spew_message(d, filename)
    struct descriptor_data *d;
    char *filename;
{
  int n, fd;
  char buf[512];
  close(reserved);
  if ((fd = open(filename, O_RDONLY)) != -1) {
    while ((n = read(fd, buf, 512)) > 0)
      queue_write(d, buf, n);
    close(fd);
    queue_write(d, "\n", 1);
  }
  reserved = open("/dev/null", O_RDWR);
}

char *strsave(s)
    const char *s;
{
  char *p;
  p = (char *) malloc(sizeof(char) * (strlen(s)+1));
  if(!p)
    panic("Out of memory");

#ifdef MEM_CHECK
  add_check("userstring");
#endif
  if (p)
    strcpy(p, s);
  return p;
}

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

int process_input(d)
    struct descriptor_data *d;
{
  int got;
  char *p, *pend, *q, *qend;
  char tbuf1[BUFFER_LEN];

  got = read(d->descriptor, tbuf1, sizeof tbuf1);
  if (got <= 0)
    return 0;
  if (!d->raw_input) {
    d->raw_input = (char *) malloc(sizeof(char) * MAX_COMMAND_LEN);
    if(!d->raw_input)
      panic("Out of memory");
#ifdef MEM_CHECK
    add_check("descriptor_raw_input");
#endif
    d->raw_input_at = d->raw_input;
  }
  p = d->raw_input_at;
  pend = d->raw_input + MAX_COMMAND_LEN - 1;
  for (q = tbuf1, qend = tbuf1 + 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((void *) d->raw_input);
#ifdef MEM_CHECK
    del_check("descriptor_raw_input");
#endif
    d->raw_input = 0;
    d->raw_input_at = 0;
  }
  return 1;
}

void set_userstring(userstring, command)
    char **userstring;
    const char *command;
{
  if (*userstring) {
    free((void *) *userstring);
#ifdef MEM_CHECK
    del_check("userstring");
#endif
    *userstring = 0;
  }
  while (*command && isascii(*command) && isspace(*command))
    command++;
  if (*command)
    *userstring = strsave(command);
}

void process_commands()
{
  int nprocessed;
  struct descriptor_data *d, *dnext;
  struct text_block *t;
  do {
    nprocessed = 0;
    for (d = descriptor_list; d; d = dnext) {
      dnext = 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;
	  if (t) {
#ifdef DEBUG
	    fprintf(stderr, "free_text_block(0x%x) at 5.\n", t);
#endif DEBUG
	    free_text_block(t);
	  }
	}
      }
    }
  } while (nprocessed > 0);
}

int do_command(d, command)
    struct descriptor_data *d;
    char *command;
{
  depth = 0;
  if (!strcmp(command, QUIT_COMMAND)) {
    return 0;
  } else if (!strncmp(command, WHO_COMMAND, strlen(WHO_COMMAND))) {
    if (d->output_prefix) {
      queue_string(d, d->output_prefix);
      queue_write(d, "\n", 1);
    }
    dump_users(d,command+strlen(WHO_COMMAND),0);
    if (d->output_suffix) {
      queue_string(d, d->output_suffix);
      queue_write(d, "\n", 1);
    }
#ifdef AT_DOING
  } else if (!strncmp(command, DOING_COMMAND, strlen(DOING_COMMAND))) {
    if (d->output_prefix) {
      queue_string(d, d->output_prefix);
      queue_write(d, "\n", 1);
    }
    dump_users(d,command+strlen(DOING_COMMAND),1);
    if (d->output_suffix) {
      queue_string(d, d->output_suffix);
      queue_write(d, "\n", 1);
    }
#endif
  } else if (!strncmp(command, PREFIX_COMMAND, strlen(PREFIX_COMMAND))) {
    set_userstring(&d->output_prefix, command + strlen(PREFIX_COMMAND));
  } else if (!strncmp(command, SUFFIX_COMMAND, strlen(SUFFIX_COMMAND))) {
    set_userstring(&d->output_suffix, command + strlen(SUFFIX_COMMAND));
#ifdef RWHO_SEND
#ifdef FULL_RWHO
  } else if (!strcmp(command, RWHO_COMMAND)) {
    dump_rusers(d);
#endif
#endif
  } else {
    if (d->connected) {
      if (d->output_prefix) {
	queue_string(d, d->output_prefix);
	queue_write(d, "\n", 1);
      }
      cplr = d->player;
      strcpy(ccom, command);
      process_command(d->player, command, d->player);
      if (d->output_suffix) {
	queue_string(d, d->output_suffix);
	queue_write(d, "\n", 1);
      }
    } else {
      if (!check_connect(d, command))
	      return 0;
    }
  }
  return 1;
}

int check_connect(d, msg)
    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)) {
    player = connect_player(user, password);
    if (player == NOTHING) {
      queue_string(d, connect_fail);
      fprintf(stderr, "FAILED CONNECT %s on descriptor %d\n",
	      user, d->descriptor);
    } else {
      fprintf(stderr, "CONNECTED %s(%d) on descriptor %d\n",
	      db[player].name, player, d->descriptor);
#ifdef AT_DOING
      d->doing[0] = '\0';
#endif
      d->connected = 1;
      d->connected_at = time((time_t *) 0);
      d->player = player;
      /* give players a message on connection */
      if (!login_allow) {
	spew_message(d, DISABLE_MSG_FILE);
	raw_notify(player, asterisk_line);
	if (cf_nologinmotd_msg && *cf_nologinmotd_msg)
	  raw_notify(player, cf_nologinmotd_msg);
	if (!Wizard(player)) {
	  raw_notify(player, asterisk_line);
	  return 0;
	} else
	  raw_notify(player, asterisk_line);
      }
      spew_message(d, CONNECT_MSG_FILE);
      /* give wizards and royalty their message, too - d'mike 7/15/91 */
      if(Hasprivs(player)) {
	spew_message(d, WIZARD_MSG_FILE);
      }
      /* set the Lastsite attribute */
      atr_add(player, "LASTSITE", d -> addr, GOD, NOTHING);
      announce_connect(player);
#ifdef USE_MAILER
      check_mail(player, 0);
#endif
      do_look_around(player);
      if (db[player].flags & HAVEN) {
	notify(player, "Your HAVEN flag is set. You cannot receive pages.");
      }
    }
  } else if (!strncmp(command, "cr", 2)) {
#ifdef WCREAT
    spew_message(d, REGISTER_MSG_FILE);
#else				/* WCREAT */
    player = create_player(user, password);
    if (player == NOTHING) {
      queue_string(d, create_fail);
      fprintf(stderr, "FAILED CREATE %s on descriptor %d\n",
	      user, d->descriptor);
    } else {
      fprintf(stderr, "CREATED %s(%d) on descriptor %d\n",
	      db[player].name, player, d->descriptor);
#ifdef AT_DOING
      d->doing[0] = '\0';
#endif
      d->connected = 1;
      d->connected_at = time((time_t *) 0);
      d->player = player;
      if(!login_allow) {
	spew_message(d, DISABLE_MSG_FILE);
        raw_notify(player, asterisk_line);
	if(cf_nologinmotd_msg && *cf_nologinmotd_msg)
	  raw_notify(player, cf_nologinmotd_msg);
        raw_notify(player, asterisk_line);
	return 0;
      }
      /* give new players a special message */
      spew_message(d, NEW_CONNECT_MSG_FILE);
      /* set the Lastsite attribute */
      atr_add(player, "LASTSITE", d -> addr, GOD, NOTHING);
      announce_connect(player);
      do_look_around(player);
    }
#endif				/* WCREAT */
  } else
    welcome_user(d);

  return 1;
}

void parse_connect(msg, command, user, pass)
    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';
}

void close_sockets()
{
  struct descriptor_data *d, *dnext;

#ifdef RWHO_SEND
  rwhocli_shutdown();
#endif
  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);
  }
}

void emergency_shutdown()
{
  close_sockets();
}

void boot_off(player)
    dbref player;
{
  struct descriptor_data *d;
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected && d->player == player) {
      shutdownsock(d);
      return;
    }
  }
}

int bailout(sig, code, scp)
    int sig;
    int code;
    struct sigcontext *scp;
{
  char tbuf1[BUFFER_LEN];

  sprintf(tbuf1, "BAILOUT: caught signal %d code %d", sig, code);
  panic(tbuf1);
  _exit(7);
  return 0;
}

void dump_users(call_by, match, doing)
    struct descriptor_data *call_by;
    char *match;
    int doing;         /* 0 if normal WHO, 1 if DOING */
{
  struct descriptor_data *d;
  int count = 0;
  time_t now;
  char tbuf1[BUFFER_LEN];
  char tbuf2[BUFFER_LEN];

  if(call_by->player < 0 || call_by->player >= db_top) {
    fprintf(stderr,"ERROR: Bogus caller of dump_users! (%d)\n",
	    call_by->player);
    return;
  }
  while(*match && *match == ' ') match++;
  now = time((time_t *) 0);

/* If a wizard/royal types "DOING" it gives him the normal player WHO. */
/* Wizard/royal WHO does not show @doings. */

  if ((doing) || !Hasprivs(call_by->player)) {
#ifdef AT_DOING
    if (poll[0] == '\0')
      strcpy(poll, "Doing");
    sprintf(tbuf2,        "Player Name          On For   Idle  %s\n", poll);
    queue_string(call_by, tbuf2);
#else
    queue_string(call_by, "Player Name          On For   Idle\n");
#endif
  }
  else {
    queue_string(call_by, "Player Name        Location    On For   Idle    [Host]\n");
  }

  for (d = descriptor_list; d; d = d->next) {
    if (d->connected) {
      if(d->player < 0 || d->player >= db_top) continue;
      if(!Dark(d->player) || Wizard(call_by->player)) ++count;
      if(match && !(string_prefix(db[d->player].name, match)))
	continue;

      if (call_by->connected && !(doing) && Hasprivs(call_by->player)) {
	sprintf(tbuf1, "%-16s   [%6d] %9s  %5s    [%s]",
		db[d->player].name,
		getloc(d->player),
		time_format_1(now - d->connected_at),
		time_format_2(now - d->last_time),
		d->addr);
	if(Dark(d->player))
	    sprintf(tbuf1+strlen(tbuf1)," (Dark)");
      } else {
	if(!Dark(d->player) || Hasprivs(call_by->player) && (doing)) {
#ifdef AT_DOING
	  sprintf(tbuf1, "%-16s %10s   %4s  %s",
#else
	  sprintf(tbuf1, "%-16s %10s   %4s",
#endif
		  db[d->player].name,
		  time_format_1(now - d->connected_at),
		  time_format_2(now - d->last_time)
#ifdef AT_DOING
	          , d->doing
#endif
		  );
	}
      }

      if(!Dark(d->player) || Hasprivs(call_by->player)) {
        queue_string(call_by, tbuf1);
        queue_write(call_by, "\n", 1);
      }

    }

 }
  sprintf(tbuf1, "There are %d players connected.\n", count);
  queue_string(call_by, tbuf1);
}

const char *time_format_1(dt)
    long dt;
{
  register struct tm *delta;
  static char buf[64];
  if (dt < 0)
    dt = 0;

  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;
}

const char *time_format_2(dt)
    long dt;
{
  register struct tm *delta;
  static char buf[64];
  if (dt < 0)
    dt = 0;

  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(player)
    dbref player;
{
  dbref loc;
  ATTR *temp;
  char tbuf1[BUFFER_LEN];

  db[player].flags |= PLAYER_CONNECT;

  if (db[player].flags & PLAYER_SUSPECT)
    raw_broadcast(WIZARD, "Broadcast: Suspect %s has connected.",
		  db[player].name);

  if ((loc = getloc(player)) == NOTHING) {
    notify(player,"You are nowhere!");
    return;
  }

  speaker = player;

#ifdef RWHO_SEND
  sprintf(tbuf1,"%d@%s", player, MUDNAME);
  rwhocli_userlogin(tbuf1, db[player].name, time((time_t *) 0));
#endif

  raw_notify(player, asterisk_line);
  if(cf_motd_msg && *cf_motd_msg)
    raw_notify(player, cf_motd_msg);
  raw_notify(player, " ");
  if(Hasprivs(player) && cf_wizmotd_msg && *cf_wizmotd_msg)
    raw_notify(player, cf_wizmotd_msg);
  raw_notify(player, asterisk_line);
      
  sprintf(tbuf1, "%s has connected.", db[player].name);
  
  notify_except(db[player].contents, player, tbuf1);
  /* added to allow player's inventory to hear a player connect */

  if(!Dark(player))
    notify_except(db[loc].contents, player, tbuf1);

  temp = atr_get(player, "ACONNECT");
  if (temp) {
    char *s = safe_uncompress(temp->value);
    parse_que(player, s, player);
    free(s);
  }
}

void announce_disconnect(player)
    dbref player;
{
  dbref loc;
  int num;
  ATTR *temp;
  struct descriptor_data *d;
  char tbuf1[BUFFER_LEN];

  if ((loc = getloc(player)) == NOTHING)
    return;

  speaker = player;

  for (num = 0, d = descriptor_list; d; d = d->next)
    if (d->connected && (d->player == player))
      num++;
  if (num < 2) {

#ifdef RWHO_SEND
    sprintf(tbuf1, "%d@%s", player, MUDNAME);
    rwhocli_userlogout(tbuf1);
#endif

    sprintf(tbuf1, "%s has disconnected.", db[player].name);
    if(!Dark(player))
      notify_except(db[loc].contents, player, tbuf1);
    /* notify contents */
    notify_except(db[player].contents,player, tbuf1);

    temp = atr_get(player, "ADISCONNECT");
    if (temp) {
      char *s = safe_uncompress(temp->value);
      parse_que(player, s, player);
      free(s);
    }

    db[player].flags &= ~PLAYER_CONNECT;

    if (db[player].flags & PLAYER_SUSPECT)
      raw_broadcast(WIZARD, "Broadcast: Suspect %s has disconnected.", 
		    db[player].name);
  }
}

#ifdef AT_DOING
extern char *reconstruct_message();

void do_doing(player, arg1, arg2)
dbref player;
char *arg1, *arg2;
{
   char *message, buf[MAX_COMMAND_LEN];
   struct descriptor_data *d;

   message = reconstruct_message(arg1, arg2);
   sprintf(buf, message);
   buf[39]='\0';
   for (d = descriptor_list; d; d=d->next)
      if (d->connected && (d->player == player))
       strcpy(d->doing,buf);
   if (strlen(message) > 39)
     notify(player, 
	    tprintf("Doing set. %d characters lost.", strlen(message) - 39));
   else
     notify(player, "Doing set.");
}

/* this sets the message which replaces "Doing" */
void do_poll(player, arg1, arg2)
  dbref player;
  char *arg1, *arg2;
{
  char *message;

  if (!Wizard(player)) {
    notify(player, "Who do you think you are, Gallup?");
    return;
  }

  message = reconstruct_message(arg1, arg2);
  strncpy(poll, message, 39);
  if (strlen(message) > 39)
    notify(player, 
	   tprintf("Poll set. %d characters lost.", strlen(message) - 39));
  else
    notify(player, "Poll set.");
}
#endif				/* AT_DOING */

#ifdef RWHO_SEND
#ifdef FULL_RWHO
void dump_rusers(call_by)
  struct descriptor_data *call_by;
{
  struct sockaddr_in addr;
  struct hostent *hp;
  char *p;
  int fd;
  int red;
  char *srv = NULL;
  int portnum = RWHOPORT;
  char tbuf1[BUFFER_LEN];

  p = srv = (char *)RWHOSERV;
  while (*p != '\0' && (*p == '.' || isdigit(*p)))
    p++;

  if(*p != '\0') {
    if((hp = gethostbyname(srv)) == (struct hostent *)0) {
       fprintf(stderr,"ERROR: unknown host %s\n",srv);
       queue_string(call_by,"Error in connecting to the RWHO server.\n");	
       return;
     }
     (void)bcopy(hp->h_addr,(char *)&addr.sin_addr,hp->h_length);
  } else {
    unsigned long   f;

    if((f = inet_addr(srv)) == -1L) {
      fprintf(stderr,"ERROR: unknown host %s\n",srv);
      queue_string(call_by,"Error in connecting to the RWHO server.\n");	
      return;
    }
    (void)bcopy((char *)&f,(char *)&addr.sin_addr,sizeof(f));
  }
  addr.sin_port = htons(portnum);
  addr.sin_family = AF_INET;

  if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0) {
    queue_string(call_by, "Socket error in connecting to rwhod. sorry.\n");
    return;
  }

  if(connect(fd,&addr,sizeof(addr)) < 0) {
    queue_string(call_by, "Connect error in connecting to rwhod. sorry.\n");
    return;
  }

  while((red = read(fd, tbuf1, sizeof(tbuf1))) > 0)
    queue_write(call_by, tbuf1, red);

  close(fd);
}
#endif /*FULL_RWHO*/

void rwho_update()
{
  struct descriptor_data *d;
  char tbuf1[BUFFER_LEN];

  rwhocli_pingalive();
  for (d = descriptor_list; d; d= d->next) {
    if(d->connected && !Dark(d->player)) {
      sprintf(tbuf1, "%d@%s", d->player, MUDNAME);
      rwhocli_userlogin(tbuf1, db[d->player].name, d->connected_at);
    }
  }
}
#endif RWHO_SEND

#ifdef LOCKOUT
int quick_wild(s, d)
    char *s;
    char *d;
{
  switch(*s) {
    case '?':
       return(wild(s+1, (*d) ? d+1 : d));
    case '*':
       return(wild(s+1, d) || ((*d) ? wild(s,d+1) : 0));
    default:
       return((UPCASE(*s) != UPCASE(*d)) ? 0 : ((*s) ? wild(s+1,d+1) : 1));
  }
}

int forbidden_site(hname)
  const char *hname;
{
  char buf[MAXHOSTNAMELEN], *newlin;
  FILE *fp;

  fp = fopen(LOCKOUT_FILE, "r");
  while ((fp != NULL) && (!feof(fp))) {
    fgets(buf, MAXHOSTNAMELEN, fp);
    /* step on the newline */
    if ((newlin = index(buf, '\n')) != NULL) *newlin = '\0';
    if (!strcasecmp(hname, buf)) {
      fclose(fp);
      return 1;
    }
  }
  fclose(fp);
  return 0;
}
const char *addrout(a)
  long a;
{
  static char buf[MAXHOSTNAMELEN];

  struct hostent *he;

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

#endif /* LOCKOUT */

dbref short_page(match)
  const char *match;
{
  struct descriptor_data *d;
  dbref who1 = NOTHING;
  int count = 0;

  for(d = descriptor_list; d; d = d->next) {
    if(d->connected) {
      if(match && !string_prefix(db[d->player].name, match))
        continue;
      if(!string_compare(db[d->player].name, match)) {
	count = 1;
	who1 = d->player;
	break;
      }
      who1 = d->player;
      count++;
    }
  }

  if(count > 1)
    return AMBIGUOUS;
  else if (count == 0)
    return NOTHING;
 
  return who1;
}


/* LWHO() function - really belongs in eval.c but needs stuff declared here */

/* this function only works for wizards and royalty */
void fun_lwho(buff, args, privs, dumm3)
    char *buff;
    char *args[10];
    dbref privs, dumm3;
{
  struct descriptor_data *d;
  char tbuf1[BUFFER_LEN];
  buff[0] = '\0';
  if (Hasprivs(privs)) {
    for (d = descriptor_list; d; d = d->next) {
      if (d->connected) {
        if (!Dark(d->player) || Hasprivs(privs)) {
          if (buff[0] == '\0')
	    sprintf(buff, "#%d", d->player);
	  else {
	    if ((strlen(buff) + 15) < BUFFER_LEN) {
	      sprintf(tbuf1, "%s #%d", buff, d->player);
	      strcpy(buff, tbuf1);
	    }
	  }
        }
      }
    }
  } else {
    notify(privs, "Permission denied.");
    strcpy(buff, "#-1");
  }
}

#ifdef IDLE_TIMEOUT
void inactivity_check()
{
  DESC *d;
  register struct tm *idle;
  time_t now;
  int check, hrs, mns;
  check = hrs = mns = 0;
  now = time((time_t *) 0);

  for (mns = INACTIVITY_LIMIT; mns > 60; mns -= 60, hrs++)
    ;

  DESC_ITER_CONN(d) {
    if (!Wizard(d->player)) {
      check = (now - d->last_time);
      idle = gmtime((time_t *) &check);
      if ((idle->tm_hour > hrs) ||
	  ((idle->tm_hour == hrs) && (idle->tm_min > mns))) {
        notify(d->player, "\n*** Inactivity timeout ***\n");
        boot_off(d->player);
      }
    }
  }
}
#endif