/* Concentrator upgraded June 1990 Robert Hood */

/* modifed Concentrator to match Fuzzy & Randoms 1.5.4 changes = 6/90 Fuzzy */

/* modified interface.c to support LOTS of people, using a concentrator */
/* May 1990, Robert Hood */

/* Warning: this file has not been upgraded to match all of the changes */
/* made to bsd.c.  January 1992, Lydia Leong */

#include "copyright.h"

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

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

#define BUFSIZE 0xFFFF

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 num;
  int connected;
  char *hostname;
  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 message {
  char *data;
  short len;
  struct message *next;
};

struct conc_list {
  struct conc_list *next;
  int sock, current, status;
  struct descriptor_data *firstd;
  struct message *first, *last;
  char *incoming, *outgoing;
  int ilen, olen;
} *firstc = 0;

void welcome_user();
void spew_message();
void writelog();
void queue_message(struct conc_list * c, char *data, int len);
struct timeval timeval_sub(struct timeval now, struct timeval then);
struct timeval msec_add(struct timeval t, int x);
struct timeval update_quotas(struct timeval last, struct timeval current);
void main_loop();
void raw_notify(dbref player, const char *msg);
void process_output(struct conc_list * c);
int process_input(struct descriptor_data * d, char *buf, int got);
void process_commands();
void dump_users(struct descriptor_data *call_by, char *match);
void free_text_block(struct text_block * t);
void main(int argc, char **argv);
void set_signals();
int msec_diff(struct timeval now, struct timeval then);
void clearstrings(struct descriptor_data * d);
void shutdownsock(struct descriptor_data * d);
struct descriptor_data *initializesock(struct sockaddr_in * a);
struct text_block *make_text_block(const char *s, int n);
void add_to_queue(struct text_queue * q, const char *b, int n);
int flush_queue(struct text_queue * q, int n);
int queue_write(struct descriptor_data * d, const char *b, int n);
int queue_string(struct descriptor_data * d, const char *s);
void freeqs(struct descriptor_data * d);
void welcome_user(struct descriptor_data * d);
char *strsave(const char *s);
void save_command(struct descriptor_data * d, const char *command);
void set_userstring(char **userstring, const char *command);
int do_command(struct descriptor_data * d, char *command);
int check_connect(struct descriptor_data * d, const char *msg);
void parse_connect(const char *msg, char *command, char *user, char *pass);
void close_sockets();
void emergency_shutdown(void);
void boot_off(dbref player);
int bailout(int sig, int code, struct sigcontext * scp);
char *time_format_1(long dt);
char *time_format_2(long dt);
void announce_connect(dbref);
void announce_disconnect(dbref);
int sigshutdown(int, int, struct sigcontext *);
#ifdef RWHO_SEND
#ifdef FULL_RWHO
void dump_rusers();
#endif
void rwho_update();
#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 *asterisk_line =
	"*****************************************************************";

int sock;
int shutdown_flag = 0;
extern dbref speaker;
extern int errno;
extern int reserved;
extern dbref db_top;
int login_allow = 1;
char ccom[BUFFER_LEN];
dbref cplr;

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

int port = TINYPORT;
int intport = INTERNAL_PORT;

void start_port()
{
  int temp;
  struct sockaddr_in sin;
  sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 1) {
    perror("socket");
    exit(-1);
  }
  temp = 1;
  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &temp, sizeof(temp));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(intport);
  sin.sin_addr.s_addr = htonl(INADDR_ANY);
  
  temp = bind(sock, (struct sockaddr *) & sin, sizeof(sin));
  if (temp < 0) {
    perror("bind");
    exit(-1);
  }
  temp = listen(sock, 5);
  if (temp < 0) {
    perror("listen");
    exit(-1);
  }
}

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

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;
  struct descriptor_data *d;
  struct conc_list *c;
  nslices = msec_diff(current, last) / COMMAND_TIME_MSEC;

  if (nslices > 0) {
    for (c = firstc; c; c = c->next)
      for (d = c->firstd; 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 raw_notify(dbref player, const char *msg)
{
  struct descriptor_data *d;
  struct conc_list *c;
#ifdef COMPRESS
  extern const char *uncompress(const char *);

  msg = uncompress(msg);
#endif				/* COMPRESS */

  for (c = firstc; c; c = c->next) {
    for (d = c->firstd; d; d = d->next) {
      if (d->connected && d->player == player) {
	queue_string(d, msg);
	queue_write(d, "\n", 1);
        process_output(c);
      }
    }
    process_output(c);
  }
}

void raw_broadcast(va_alist)
va_dcl
{
  char buff[BUFFER_LEN];
  va_list args;
  int inflags;
  char *template;
  struct descriptor_data *d;
  struct conc_list *c;

  va_start(args);
  inflags = va_arg(args, int);
  template = va_arg(args, char *);

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

  vsprintf(buff, template, args);
  for (c = firstc; c; c = c->next) {
    for (d = c->firstd; d; d = d->next) {
      if (d->connected && (db[d->player].flags & inflags) == inflags) {
	queue_string(d, buff);
	queue_write(d, "\n", 1);
        process_output(c);
      }
    }
    process_output(c);
  }
}

int process_input(d, buf, got)
    struct descriptor_data *d;
    char *buf;
    int got;
{
  char *p, *pend, *q, *qend;
  d->last_time = time(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 = 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((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 process_commands()
{
  int nprocessed;
  struct descriptor_data *d, *dnext, *dlast;
  struct conc_list *c;
  struct text_block *t;
  char header[4];
  do {
    nprocessed = 0;
    for (c = firstc; c; c = c->next) {
      dlast = 0;
      for (d = c->firstd; 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);
	    process_output(c);
            header[0] = 0;
            header[1] = 2;
            header[2] = d->num;
            queue_message(c, header, 3);
            process_output(c);
            if (dlast)
              dlast->next = dnext;
            else
              c->firstd = dnext;
            free((void *)d);
	    break;
	  } else {
	    d->input.head = t->nxt;
	    if (!d->input.head)
	      d->input.tail = &d->input.head;
	    free_text_block(t);
	  }
	}
	dlast = d;
      }
    }
  } while (nprocessed > 0);
}

void dump_users(struct descriptor_data *call_by, char *match)
{
  struct descriptor_data *d;
  struct conc_list *c;
  time_t now;
  int count = 0;
  char tbuf1[BUFFER_LEN];


  while (*match && isspace(*match))
    match++;
  if (!*match)
    match = NULL;

  time(&now);
  if(Wizard(call_by->player)) {
#ifdef AT_DOING
    queue_string(call_by, "Player Name        Location    On For   Idle    [Host
] / Doing\n");
#else
    queue_string(call_by, "Player Name        Location    On For   Idle    [Host]\n");
#endif
  } else {
#ifdef AT_DOING
    queue_string(call_by, "Player Name          On For   Idle  Doing\n");
#else
    queue_string(call_by, "Player Name          On For   Idle\n");
#endif
  }
  for (c = firstc; c; c = c->next) {
    for (d = c->firstd; 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 && Wizard(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->hostname);
          if(Dark(d->player))
            sprintf(tbuf1+strlen(tbuf1)," (Dark)");
        } else {
          if(!Dark(d->player)) {
#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) || Wizard(call_by->player)) {
          queue_string(call_by, tbuf1);
          queue_write(call_by, "\n", 1);
	}
#ifdef AT_DOING
        if(Wizard(call_by->player) && d->doing[0]) {
          sprintf(tbuf1, "%46s  %s", " ", d->doing);
          queue_string(call_by, tbuf1);
          queue_write(call_by, "\n", 1);
        }
#endif
      }
    }
  }
  sprintf(tbuf1, "There %s %d user%s connected\n", (count == 1 ? "is" : "are"),
	  count, (count == 1 ? "" : "s"));
  queue_string(call_by, tbuf1);
}

void free_text_block(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
}

#ifndef BOOLEXP_DEBUGGING
void main(int argc, char **argv)
{
  int pid;
  const char *def_db_in = DEF_DB_IN;
  const char *def_db_out = DEF_DB_OUT;

  if(argc > 1) {
    --argc;
    def_db_in  = *++argv;
  }
  if(argc > 1) {
    --argc;
    def_db_out = *++argv;
  }

  if(argc > 1) {
    --argc;
    port = atoi(*++argv);
  }
  if(argc > 1) {
    --argc;
    intport = atoi(*++argv);
  }

  srand(time(NULL));

  reserved = open("/dev/null", O_RDWR);

  if (init_game(def_db_in, def_db_out) < 0) {
    writelog("ERROR: Couldn't load %s! Exiting.\n", def_db_in);
    exit(2);
  }
  pid = vfork();
  if (pid < 0) {
    perror("fork");
    exit(-1);
  }
  if (pid == 0) {
    char pstr[32], istr[32], clvl[32];
    /* Add port argument to concentrator */
    sprintf(pstr, "%d", port);
    sprintf(istr, "%d", intport);
    sprintf(clvl, "%d", 1);
    execl("concentrate", "conc", pstr, istr, clvl, 0);
  }
  set_signals();
  start_port(port);
  main_loop();
  close_sockets();
  dump_database();
  exit(0);
}
#endif

void set_signals(void)
{
  signal(SIGPIPE, SIG_IGN);

  signal(SIGINT, (void *) bailout);
  signal(SIGTERM, (void *) bailout);
}

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

void clearstrings(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(struct descriptor_data *d)
{
  struct conc_list *c;
  struct descriptor_data *d1;

  if (d->connected) {
    spew_message(d, LEAVE_MSG_FILE);
    writelog("DISCONNECT descriptor %d,%d player %s(%d)\n", d->descriptor,
	     d->num, db[d->player].name, d->player);
    announce_disconnect(d->player);
  } else {
    writelog("DISCONNECT descriptor %d,%d never connected\n", d->descriptor,
	     d->num);
  }
  for(c = firstc; c; c = c->next) {
    for(d1 = c->firstd; d1; d1 = d1->next) {
      process_output(c);
    }
  }
  clearstrings(d);
  freeqs(d);
}

struct descriptor_data *
 initializesock(struct sockaddr_in * a)
{
  struct descriptor_data *d;

  d = (struct descriptor_data *) malloc(sizeof(struct descriptor_data));
  if(!d)
    panic("Out of memory.");
#ifdef MEM_CHECK
  add_check("descriptor");
#endif
  d->connected = 0;
  d->player = 0;
  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->address = *a;		/* This will be the address of the
				 * concentrator */
  d->hostname = "";		/* This will be set during connect */
#ifdef AT_DOING
  d->doing[0] = '\0';
#endif
  welcome_user(d);
  return d;
}

struct text_block *
 make_text_block(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 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));
}

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((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(struct descriptor_data * d)
{
  spew_message(d, WELCOME_MSG_FILE);
}

void spew_message(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(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(struct descriptor_data * d, const char *command)
{
  add_to_queue(&d->input, command, strlen(command) + 1);
}

void set_userstring(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);
}

int do_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));
    if (d->output_suffix) {
      queue_string(d, d->output_suffix);
      queue_write(d, "\n", 1);
    }
  } else if (d->connected &&
	     !strncmp(command, PREFIX_COMMAND, strlen(PREFIX_COMMAND))) {
    set_userstring(&d->output_prefix, command + strlen(PREFIX_COMMAND));
  } else if (d->connected &&
	     !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(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);
      writelog("FAILED CONNECT %s on descriptor %d,%d\n", user, d->descriptor, d->num);
    } else {
      writelog("CONNECTED %s(%d) on descriptor %d,%d %s\n",
	       db[player].name, player, d->descriptor, d->num, d->hostname);
#ifdef AT_DOING
      d->doing[0] = '\0';
#endif
      d->connected = 1;
      d->connected_at = time((time_t *) 0);
      d->player = player;
      if(!login_allow && !Wizard(player)) {
        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;
      }
      spew_message(d, CONNECT_MSG_FILE);
      if(Wizard(player)) {
        spew_message(d, WIZARD_MSG_FILE);
      }
      /* set the Lastsite attribute */
      atr_add(player, "LASTSITE", d -> addr, GOD, NOTHING);
      announce_connect(player);
      do_look_around(player);
      /* give wizards their message, too - d'mike 7/15/91 */
      if (db[player].flags & HAVEN) {
        notify(player, "Your HAVEN flag is set. You cannot receive pages.");
      }
    }
  } else if (!strncmp(command, "cr", 2)) {
#ifndef WCREAT
    player = create_player(user, password);
    if (player == NOTHING) {
      queue_string(d, create_fail);
      writelog("FAILED CREATE %s on descriptor %d,%d %s\n",
	       user, d->descriptor, d->num, d->hostname);
    } else {
      writelog("CREATED %s(%d) on descriptor %d,%d %s\n",
	       db[player].name, player, d->descriptor, d->num, d->hostname);
#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);
    }
#else
      spew_message(d, REGISTER_MSG_FILE);
#endif				/* WCREAT */
  } else {
    welcome_user(d);
  }
  return 1;
}

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

void close_sockets(void)
{
  struct conc_list *c;

#ifdef RWHO_SEND
  rwhocli_shutdown();
#endif
  for (c = firstc; c; c = c->next) {
    /* conc.c now handles printing the Going Down - Bye message */
    shutdown(c->sock, 0);
    close(c->sock);
  }
  close(sock);
}

void emergency_shutdown(void)
{
  close_sockets();
}

int bailout(int sig, int code, struct sigcontext *scp)
{
  int i;
  writelog("BAILOUT: caught signal %d code %d", sig, code);
  panic("PANIC on spurious signal");
  _exit(7);
  return 0;
}

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

  delta = gmtime(&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[64];

  if(dt < 0) dt = 0;
  delta = gmtime(&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;
  ATTR *temp;
  char buf[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(buf,"%d@%s", player, MUDNAME);
  rwhocli_userlogin(buf, 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(Wizard(player) && cf_wizmotd_msg && *cf_wizmotd_msg)
    raw_notify(player, cf_wizmotd_msg);
  raw_notify(player, asterisk_line);

  sprintf(buf, "%s has connected.", db[player].name);

  notify_except(db[player].contents, player, buf);
  /* added to allow player's inventory to hear a player connect */

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

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

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

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

  speaker = player;

  for (num = 0, c = firstc; c; c = c->next)
    for (d = c->firstd; 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);
   notify(player, "Set.");
}
#endif

#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;
  struct conc_list *c;
  char tbuf1[BUFFER_LEN];

  rwhocli_pingalive();
  for (c = firstc; c; c = c->next) {
    for (d = c->firstd; 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;
  struct conc_list *c;
  dbref who1 = NOTHING;
  int count = 0;

  for(c = firstc; c; c = c->next) {
    for(d = c->firstd; 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;
}

void main_loop()
{
  struct message *ptr;
  int found, newsock, lastsock, len;
  int accepting = 1;
  struct timeval tv;
  struct sockaddr_in sin;
  fd_set in, out;
  char buf[BUFFER_LEN];
  struct conc_list *c, *tempc, *nextc, *lastc;
  struct descriptor_data *d, *tempd;
  struct timeval last_slice, current_time;
  struct timeval next_slice;
  struct timeval slice_timeout, timeout;
  short templen;
  time_t tt;
  extern void dispatch();

  lastsock = sock + 1;
  while (!shutdown_flag) {
    gettimeofday(&current_time, (struct timezone *) 0);
    last_slice = update_quotas(last_slice, current_time);

    process_commands();

    if (shutdown_flag)
      break;

    dispatch();

    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(&in);
    FD_ZERO(&out);

    FD_SET(sock, &in);
    for (c = firstc; c; c = c->next)
      process_output(c);
    for (c = firstc; c; c = c->next)
      if (c->sock) {
	if (c->ilen < BUFSIZE)
	  FD_SET(c->sock, &in);
	len = c->first ? c->first->len : 0;
	while (c->first && ((c->olen + len + 2) < BUFSIZE)) {
	  templen = c->first->len;
	  bcopy(&templen, c->outgoing + c->olen, 2);
	  bcopy(c->first->data, c->outgoing + c->olen + 2, len);
	  c->olen += len + 2;
	  ptr = c->first;
	  c->first = ptr->next;
	  free((void *)ptr->data);
	  free((void *)ptr);
	  if (c->last == ptr)
	    c->last = 0;
	  len = c->first ? c->first->len : 0;
	}
	if (c->olen)
	  FD_SET(c->sock, &out);
      } else
	timeout = slice_timeout;

    if((found = select(lastsock, &in, &out, (fd_set *) 0, &timeout)) < 0) {
      if(errno != EINTR) {
	perror("select");
	return;
      }
    } else {
      if(!found) {
        do_top();
        do_top();
        do_top();
        continue;
      }
      if (accepting && FD_ISSET(sock, &in)) {
        len = sizeof(sin);
        newsock = accept(sock, (struct sockaddr *) & sin, &len);
        if (newsock >= 0) {
	  if (newsock >= lastsock)
	    lastsock = newsock + 1;
	  if (fcntl(newsock, F_SETFL, FNDELAY) == -1) {
	    perror("make_nonblocking: fcntl");
	  }
	  tempc = (struct conc_list *)malloc(sizeof(struct conc_list));
	  if(!tempc)
	    panic("Out of memory.");
	  tempc->next = firstc;
	  tempc->firstd = 0;
	  tempc->first = 0;
	  tempc->last = 0;
	  tempc->status = 0;
	  /* Imcomming and outgoing I/O buffers */
	  tempc->incoming = (char *)malloc(BUFSIZE);
	  if(!tempc->incoming)
	    panic("Out of memory.");
	  tempc->ilen = 0;
          tempc->outgoing = (char *)malloc(BUFSIZE);
          if(!tempc)
            panic("Out of memory.");
	  tempc->olen = 0;
  	  firstc = tempc;
	  firstc->sock = newsock;
	  writelog("CONCENTRATOR CONNECT: sock %d, addr %x\n", newsock,
		   sin.sin_addr.s_addr);
        }
      }
      for (c = firstc; c; c = nextc) {
        nextc = c->next;
#ifdef CHECKC
        if (!(c->sock))
  	  writelog("CONSISTENCY CHECK: Concentrator found with null socket #\n");
#endif
        if ((FD_ISSET(c->sock, &in)) && (c->ilen < BUFSIZE)) {
	  len = recv(c->sock, c->incoming + c->ilen,
		     BUFSIZE - c->ilen, 0);
	  if (len == 0) {
	    struct message *mptr, *tempm;
	    writelog("CONCENTRATOR DISCONNECT: %d\n", c->sock);
	    close(c->sock);
	    d = c->firstd;
	    while (d) {
	      shutdownsock(d);
	      tempd = d;
	      d = d->next;
	      free((void *)tempd);
	    }
	    if (firstc == c)
	      firstc = firstc->next;
	    else
	      lastc->next = c->next;
	    free((void *)c->incoming);
	    free((void *)c->outgoing);
	    mptr = c->first;
	    while (mptr) {
	      tempm = mptr;
	      mptr = mptr->next;
	      free((void *)mptr->data);
	      free((void *)mptr);
	    }
	    free((void *)c);
	    break;
	  } else if (len < 0) {
	    writelog("recv: %s\n", strerror(errno));
	  } else {
	    int num;
	    c->ilen += len;
	    while (c->ilen > 2) {
	      bcopy(c->incoming, &templen, 2);
#ifdef CHECKC
	      if (templen < 1)
	        writelog("CONSISTENCY CHECK: Message recived with length < 1\n");
#endif
	      if (c->ilen >= (templen + 2)) {
	        num = *(c->incoming + 2);
	        /* Is it coming from the command user #? */
	        if (num == 0) {
	  	  /* Proccess commands */
	  	  switch (*(c->incoming + 3)) {
		    case 1:	/* connect */
		      tt = time((time_t *) 0);
		      d = initializesock(&sin);
		      d->descriptor = c->sock;
		      d->next = c->firstd;
		      c->firstd = d;
		      d->num = *(c->incoming + 4);
		      d->hostname= (char *)malloc(templen - 5);
		      if(!d->hostname)
		        panic("Out of memory.");
		      bcopy(c->incoming + 9, d->hostname,
		  	    templen - 6);
		      *(d->hostname + templen - 7) = 0;
		      writelog("USER CONNECT %d,%d from host %s at %s\n",
			       c->sock, d->num, d->hostname, ctime(&tt));
		      break;
		    case 2:	/* disconnect */
		      tempd = 0;
		      d = c->firstd;
		      num = *(c->incoming + 4);
		      while (d) {
		        if (d->num == num) {
			  writelog("USER ABORTED CONNECTION %d,%d\n", c->sock,
				   d->num);
			  shutdownsock(d);
			  if (c->firstd == d)
			    c->firstd = d->next;
			  else
			    tempd->next = d->next;
			  free((void *)d);
			  break;
		        }
		        tempd = d;
		        d = d->next;
		      }
#ifdef CHECKC
		      if (!d)
		        writelog("CONSISTENCY CHECK: Disconnect Received for unknown user %d,%d\n", c->sock, num);
#endif
		      break;

		    /*
		     * This take a message from a concentrator, and logs it
		     * in the log file
		     */
		     case 4:
		       {
		         bcopy(c->incoming + 4, buf,
			       templen - 2);
		         *(buf + templen - 1) = 0;
		         writelog(buf);
		         break;
		       }
#ifdef CHECKC
		     default:
		       writelog("CONSISTENCY CHECK: Received unknown command from concentrator\n");
#endif
		   }
	         } else {
		   d = c->firstd;
		   while (d) {
		     if (d->num == num) {
		       process_input(d, c->incoming + 3,
				     templen - 1);
		       break;
		     }
		     d = d->next;
	   	   }
#ifdef CHECKC
		   if (!d)
		     writelog("CONSISTENCY CHECK: Message received for unknown user %d,%d\n", c->sock, num);
#endif
	         }
	         bcopy(c->incoming + templen + 2, c->incoming,
		       (c->ilen - templen - 2));
	         c->ilen = c->ilen - templen - 2;
	       } else
	         break;
	     }
	   }
         }
         lastc = c;
       }
    }
    /* Send data loop */
    for (c = firstc; c; c = c->next) {
      if (FD_ISSET(c->sock, &out)) {
	if (c->olen) {
	  len = send(c->sock, c->outgoing, c->olen, 0);
	  if (len > 0) {
	    c->olen -= len;
	    bcopy(c->outgoing + len, c->outgoing, c->olen);
	  }
	}
      }
    }
  }
}

void process_output(struct conc_list * c)
{
  struct descriptor_data *d;
  struct text_block **qp, *cur;
  short templen;
  for (d = c->firstd; d; d = d->next) {
    qp = &d->output.head;
    cur = *qp;
    if (cur) {
      if (cur->nchars < 512) {
	if ((c->olen + cur->nchars + 3) < BUFSIZE) {
	  templen = cur->nchars + 1;
	  bcopy(&templen, c->outgoing + c->olen, 2);
	  *(c->outgoing + c->olen + 2) = d->num;
	  strncpy(c->outgoing + c->olen + 3, cur->start, cur->nchars);
	  d->output_size -= cur->nchars;
	  c->olen += cur->nchars + 3;
	  if (!cur->nxt)
	    d->output.tail = qp;
	  *qp = cur->nxt;
	  free_text_block(cur);
	}
      } else {
	if ((c->olen + 512 + 3) < BUFSIZE) {
	  templen = 512 + 1;
	  bcopy(&templen, c->outgoing + c->olen, 2);
	  *(c->outgoing + c->olen + 2) = d->num;
	  strncpy(c->outgoing + c->olen + 3, cur->start, 512);
	  d->output_size -= 512;
	  c->olen += 512 + 3;
	  cur->nchars -= 512;
	  cur->start += 512;
	}
      }
    }
  }
}

void boot_off(dbref player)
{
  struct conc_list *c;
  struct descriptor_data *d, *lastd;
  char header[4];
  for (c = firstc; c; c = c->next) {
    lastd = 0;
    for (d = c->firstd; d; d = d->next) {
      if (d->connected && d->player == player) {
	shutdownsock(d);
	header[0] = 0;
	header[1] = 2;
	header[2] = d->num;
	queue_message(c, header, 3);
	process_output(c);
	if (lastd)
	  lastd->next = d->next;
	else
	  c->firstd = d->next;
	free((void *) d);
	return;
      }
      lastd = d;
    }
  }
}

void queue_message(struct conc_list * c, char *data, int len)
{
  struct message *ptr;
  ptr = (struct message *) malloc(sizeof(struct message));
  if(!ptr)
    panic("Out of memory.");
  ptr->data = (char *) malloc(len);
  if(!ptr->data)
    panic("Out of memory.");
  ptr->len = len;
  bcopy(data, ptr->data, len);
  ptr->next = 0;
  if (c->last == 0)
    c->first = ptr;
  else
    c->last->next = ptr;
  c->last = ptr;
}

void writelog(va_alist)
va_dcl
{
   char buf[BUFFER_LEN];
   va_list args;
   char *fmt;

   va_start(args);
   fmt = va_arg(args, char *);

   (void) vsprintf(buf, fmt, args);
   buf[BUFFER_LEN -1] = '\0';
   fprintf(stderr, buf);
}