/* 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 */

/* #define CHECKC	/* consistency checking */

#include <stdio.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 "config.h"
#include "db.h"
#include "interface.h"

#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;
  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;
  struct sockaddr_in address;	       /* added 3/6/90 SCG */
  char           *hostname;	       /* 5/18/90 - Fuzzy */
  struct descriptor_data *next;
};

#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))

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            queue_message(struct conc_list * c, char *data, int len);
void            start_log();
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();
int             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 * e, char *user);
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);
void            do_motd(dbref);
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);
void            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);
#ifdef CONNECT_MESSAGES
void            announce_connect(dbref);
void            announce_disconnect(dbref);
#endif				       /* CONNECT_MESSAGES */
int             sigshutdown(int, int, struct sigcontext *);

int             logsynch();
char           *logfile = LOG_FILE;

int             debug;
void            chg_userid();
void            file_date(struct descriptor_data * d, const char *file);

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

int             sock;
int             shutdown_flag = 0;

int             port = TINYPORT;
int             intport = INTERNAL_PORT;


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

int
                notify(dbref player2, const char *msg)
{
  struct descriptor_data *d;
  struct conc_list *c;
  int             retval = 0;

#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 == player2)
      {
	queue_string(d, msg);
	queue_write(d, "\n", 1);
	retval++;
      }
    }
  return (retval);
}

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)
  {
    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            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))
	  {
	    header[0] = 0;
	    header[1] = 2;
	    header[2] = d->num;
	    queue_message(c, header, 3);
	    if (dlast)
	      dlast->next = dnext;
	    else
	      c->firstd = dnext;
	    shutdownsock(d);
	    FREE(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 * e, char *user)
{
  static struct conc_list *rwclist[NOFILE];
  static struct descriptor_data *rwdlist[NOFILE];
  int             ccount, dcount, dloop, cloop;

  struct descriptor_data *d;
  struct conc_list *c;
  long            now;
  int             counter = 0;
  static int	  maxcounter = 0;
  int             wizard, reversed, tabular;
  char            buf[1024];


# ifdef DO_WHOCHECK
  writelog("WHO CHECK %d\n", sizeof(rwclist));
  writelog("WHO CHECK %d\n", sizeof(rwdlist));
# endif

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

  reversed = e->connected && Flag(e->player, REVERSED_WHO);
  tabular = e->connected && Flag(e->player, TABULAR_WHO);

  time(&now);

  queue_string(e, tabular ? "Player Name          On For Idle\n" :
	       "Current Players:\n");
#ifdef GOD_MODE
  wizard = e->connected && God(e->player);
#else  GOD_MODE
  wizard = e->connected && Wizard(e->player);
#endif GOD_MODE

  if (reversed)
  {
    ccount = 0;
    for (c = firstc; c; c = c->next)
      rwclist[ccount++] = c;
    for (cloop = ccount - 1; cloop >= 0; --cloop)
    {
      dcount = 0;
      for (d = rwclist[cloop]->firstd; d; d = d->next)
	rwdlist[dcount++] = d;
      for (dloop = dcount - 1; dloop >= 0; --dloop)
      {
	d = rwdlist[dloop];
	if (d->connected &&
	    ++counter && /* Count everyone connected */
	    (!user || string_prefix(db[d->player].name, user)))
	{
	  if (tabular)
	  {
	    sprintf(buf, "%-16s %10s %4s",
		    db[d->player].name,
		    time_format_1(now - d->connected_at),
		    time_format_2(now - d->last_time));

	    if (wizard)
	      sprintf(buf, "%s  %s", buf, d->hostname);
	  } else
	  {
	    sprintf(buf, "%s idle %d seconds",
		    db[d->player].name, now - d->last_time);
	    if (wizard)
	      sprintf(buf, "%s from host %s", buf, d->hostname);
	  }
	  strcat(buf, "\n");
	  queue_string(e, buf);
	}
      }
    }
  } else
  {
    for (c = firstc; c; c = c->next)
    {
      for (d = c->firstd; d; d = d->next)
      {
	if (d->connected &&
	    ++counter && /* Count everyone connected */
	    (!user || string_prefix(db[d->player].name, user)))
	{
	  if (tabular)
	  {
	    sprintf(buf, "%-16s %10s %4s",
		    db[d->player].name,
		    time_format_1(now - d->connected_at),
		    time_format_2(now - d->last_time));

	    if (wizard)
	      sprintf(buf, "%s  %s", buf, d->hostname);
	  } else
	  {
	    sprintf(buf, "%s idle %d seconds",
		    db[d->player].name, now - d->last_time);
	    if (wizard)
	      sprintf(buf, "%s from host %s", buf, d->hostname);
	  }
	  strcat(buf, "\n");
	  queue_string(e, buf);
	}
      }
    }
  }
  
  if (counter > maxcounter)
  { maxcounter = counter;
    if (counter > 30)
    { writelog ("%d users logged in\n", counter); }
  }
  
  sprintf(buf, "%d user%s connected\n", counter,
	  counter == 1 ? " is" : "s are");
  queue_string(e, buf);
}

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

#ifndef BOOLEXP_DEBUGGING
void main(int argc, char **argv)
{
  int             pid;

  if (argc < 3)
  {
    fprintf(stderr, "Usage: %s infile dumpfile [port iport logfile]\n", *argv);
    exit(1);

  }
  if (argc > 3)
    port = atoi(argv[3]);
  if (argc > 4)
    intport = atoi(argv[4]);
  if (argc > 5)
    logfile = argv[5];

  start_log();

  if (init_game(argv[1], argv[2]) < 0)
  {
    writelog("INIT: Couldn't load %s\n", argv[1]);
    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 start_log()
{
#ifdef DETACH
  if              (!debug)
  {
    int             i;

    if (fork() != 0)
      exit(0);

    i = open("/dev/tty", O_RDWR, 0);
    if (i != -1)
    {
      ioctl(i, TIOCNOTTY, 0);
      close(i);
    }
  }
  freopen(logfile, "a", stderr);
  setbuf(stderr, NULL);
#endif				       /* DETACH */
}

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

  signal(SIGINT, (void *)sigshutdown);
  signal(SIGTERM, (void *)sigshutdown);

#ifdef DETACH
  signal(SIGUSR2, (void *)logsynch);
#else				       /* DETACH */
  signal(SIGUSR2, (void *)bailout);
#endif				       /* DETACH */

  if (debug)
    return;

# ifdef NOCOREDUMP
  signal(SIGQUIT, (void *)bailout);
  signal(SIGILL, (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);
# endif
}

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(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)
  {
                    writelog("DISCONNECT descriptor %d,%d player %s(%d)\n", d->descriptor, d->num, db[d->player].name, d->player);
#ifdef CONNECT_MESSAGES
    announce_disconnect(d->player);
#endif				       /* CONNECT_MESSAGES */
  } else
  {
    writelog("DISCONNECT descriptor %d,%d never connected\n", d->descriptor, d->num);
  }
  clearstrings(d);
  freeqs(d);
}

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

  MALLOC(d, struct descriptor_data, 1);
  d->connected = 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 */

  welcome_user(d);
  return d;
}

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
                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(d->raw_input);
  d->raw_input = 0;
  d->raw_input_at = 0;
}

void
                welcome_user(struct descriptor_data * d)
{
  queue_string(d, WELCOME_MESSAGE);
  file_date(d, NEWS_FILE);
# ifdef CONNECT_FILE
  do_connect_msg(d, CONNECT_FILE);
# endif
}

void
                goodbye_user(struct descriptor_data * d)
{
                  queue_string(d, 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);
}

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

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, 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)))
  {
#ifdef ROBOT_MODE
    if (!Robot(d->player))
    {
#ifndef TINKER
      notify(d->player,
	     "Only robots can use OUTPUTPREFIX; contact a Wizard.");
#else TINKER
      notify(d->player,
	     "Only robots can use OUTPUTPREFIX; contact a Tinker.");
#endif TINKER
      return 1;
    }
    if (!d->connected)
      return 1;
#endif ROBOT_MODE
    set_userstring(&d->output_prefix, command + strlen(PREFIX_COMMAND));
  } else
    if (d->connected &&
	!strncmp(command, SUFFIX_COMMAND, strlen(SUFFIX_COMMAND)))
  {
#ifdef ROBOT_MODE
    if (!Robot(d->player))
    {
#ifndef TINKER
      notify(d->player,
	     "Only robots can use OUTPUTSUFFIX; contact a Wizard.");
#else TINKER
      notify(d->player,
	     "Only robots can use OUTPUTSUFFIX; contact a Tinker.");
#endif TINKER
      return 1;
    }
#endif ROBOT_MODE
    set_userstring(&d->output_suffix, command + strlen(SUFFIX_COMMAND));
  } else
  {
    if (d->connected)
    {
      if (d->output_prefix)
      {
	queue_string(d, d->output_prefix);
	queue_write(d, "\n", 1);
      }
      process_command(d->player, command);
      if (d->output_suffix)
      {
	queue_string(d, d->output_suffix);
	queue_write(d, "\n", 1);
      }
    } else
    {
      check_connect(d, command);
    }
  }
  return 1;
}

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))
  {
    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);
      d->connected = 1;
      d->connected_at = time(NULL);
      d->player = player;
      do_motd(player);
      do_look_around(player);
#ifdef CONNECT_MESSAGES
      announce_connect(player);
#endif				       /* CONNECT_MESSAGES */
    }
  } else
  if (!strncmp(command, "cr", 2))
  {
#ifndef REGISTRATION
    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);
      d->connected = 1;
      d->connected_at = time(0);
      d->player = player;
      do_motd(player);
      do_look_around(player);
#ifdef CONNECT_MESSAGES
      announce_connect(player);
#endif				       /* CONNECT_MESSAGES */
    }
#else
    queue_string(d, REGISTER_MESSAGE);
#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';
}

void
                close_sockets(void)
{
  struct descriptor_data *d, *dnext;
  struct conc_list *c;
  char            header[4];

  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)
{
  long           *ptr;
  int             i;

  writelog("BAILOUT: caught signal %d code %d", sig, code);
  ptr = (long *)scp;
  for (i = 0; i < sizeof(struct sigcontext); i++)
    writelog("  %08lx\n", *ptr);
  panic("PANIC on spurious signal");
  _exit(7);
  return 0;
}

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

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

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

#ifdef CONNECT_MESSAGES
void
                announce_connect(dbref player)
{
  dbref           loc;
  char            buf[BUFFER_LEN];

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

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

  notify_except(db[loc].contents, player, buf);
}

void
                announce_disconnect(dbref player)
{
  dbref           loc;
  char            buf[BUFFER_LEN];

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

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

  notify_except(db[loc].contents, player, buf);
}

#endif				       /* CONNECT_MESSAGES */

int
                sigshutdown(int sig, int code, struct sigcontext * scp)
{
                  writelog("SHUTDOWN: on signal %d code %d\n", sig, code);
  shutdown_flag = 1;
  return 0;
}

#ifdef DETACH
int
                logsynch()
{
                  freopen(logfile, "a", stderr);
  setbuf(stderr, NULL);
  writelog("log file reopened\n");
  return 0;
}

#endif				       /* DETACH */

#include <sys/stat.h>

void
                file_date(struct descriptor_data * d, char *file)
{
  static char     buf[80];
  extern char    *ctime(long *clock);
  struct stat     statb;
  char           *tstring;
  char           *cp;

  if (stat(file, &statb) == -1)
    return;

  tstring = ctime(&statb.st_mtime);
  if ((cp = (char *)index(tstring, '\n')) != NULL)
    *cp = '\0';

  strcpy(buf, "News last updated ");
  strcat(buf, tstring);
  strcat(buf, "\n\n");

  queue_string(d, (char *)buf);
}

void            main_loop()
{
  struct message *ptr;
  int             found, newsock, lastsock, len, loop;
  int             accepting;
  struct timeval  tv;
  struct sockaddr_in sin;
  fd_set          in, out;
  char            data[1025], *p1, *p2, buffer[1025], header[4];
  struct conc_list *c, *tempc, *nextc, *lastc;
  struct descriptor_data *d, *tempd, *nextd;
  struct timeval  last_slice, current_time;
  struct timeval  next_slice;
  struct timeval  slice_timeout;
  short           templen;

  accepting = 1;
  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;

    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(ptr->data);
	  FREE(ptr);
	  if (c->last == ptr)
	    c->last = 0;
	  len = c->first ? c->first->len : 0;
	}
	if (c->olen)
	  FD_SET(c->sock, &out);
      }
    tv.tv_sec = 1000;
    tv.tv_usec = 0;

    found = select(lastsock, &in, &out, (fd_set *) 0, &tv);
    if (found < 0)
      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");
	}
	MALLOC(tempc, struct conc_list, 1);
	tempc->next = firstc;
	tempc->firstd = 0;
	tempc->first = 0;
	tempc->last = 0;
	tempc->status = 0;
	/* Imcomming and outgoing I/O buffers */
	MALLOC(tempc->incoming, char, BUFSIZE);
	tempc->ilen = 0;
	MALLOC(tempc->outgoing, char, BUFSIZE);
	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))
      {
	int             i;
	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(tempd);
	  }
	  if (firstc == c)
	    firstc = firstc->next;
	  else
	    lastc->next = c->next;
	  FREE(c->incoming);
	  FREE(c->outgoing);
	  mptr = c->first;
	  while (mptr)
	  {
	    tempm = mptr;
	    mptr = mptr->next;
	    FREE(mptr->data);
	    FREE(mptr);
	  }
	  FREE(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 lenght < 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 */
		  d = initializesock(&sin);
		  d->descriptor = c->sock;
		  d->next = c->firstd;
		  c->firstd = d;
		  d->num = *(c->incoming + 4);
		  MALLOC(d->hostname, char,
			 templen - 5);
		  bcopy(c->incoming + 9, d->hostname,
			templen - 6);
		  *(d->hostname + templen - 7) = 0;
#ifdef DEBUG
		  writelog("USER CONNECT %d,%d from host %s\n", c->sock, d->num, d->hostname);
#endif
		  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(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:
		  {
		    char            buffer[2048];
		    bcopy(c->incoming + 4, buffer,
			  templen - 2);
		    *(buffer + templen - 1) = 0;
		    writelog(buffer);
		    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)
      {
	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;
	shutdownsock(d);
	FREE(d);
	return;
      }
      lastd = d;
    }
  }
}

void            queue_message(struct conc_list * c, char *data, int len)
{
  struct message *ptr;

  MALLOC(ptr, struct message, 1);
  MALLOC(ptr->data, char, len);
  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;
}

int do_connect_msg(struct descriptor_data * d, const char *filename)
{
  FILE           *f;
  char            buf[BUFFER_LEN];
  char           *p;

  if ((f = fopen(filename, "r")) == NULL)
  {
    return (0);
  } else
  {
    while (fgets(buf, sizeof buf, f))
    {
      queue_string(d, (char *)buf);

    }
    fclose(f);
    return (1);
  }
}