/*

    Copyright (C), 1991, Marcus J. Ranum. All rights reserved.

to make:
    cc [-DSETSID -DDAEMON] -o mwhod mwhod.c

    MUD rwho daemon. This operates with 2 sockets open, one for datagrams
containing update information for the database, the other a stream connection
for users to connect to and download information about what MUDs are up and
who is on.

    mwhods can share their databases between eachother by being defined
as PEERs in the configuration file. The config file specifies a list of MUDs
or servers that we trust and will accept data from, along with passwords to
use when communicating with them. Information is stored about the number of
times a data item has been passed, and the generation count is incremented
each time the datum is re-forwarded. After a certain number of generations,
data will no longer be forwarded.

    The server makes a traverse of its database periodically and expires
all information that it has not received an update for in a given period of
time. This is settable arbitrarily, but should not be too high a value.
*/

#include "os.h"
#ifdef WIN32
#include    "getopt.h"
#endif

/* change this? */
static char connectmsg[] = "Connected to Mud RWHO server...\n";

FILE *logfile = NULL;

/* hear nothing from a MUD for more than this, expire it */
#define MUDTTL          800;
#define PLYTTL          500;

/* name of server, in case we need to propagate */
char *myname = (char *) 0;

/* ports to listen at */
#define DGRAMPORT       6888
#define STREAMPORT      6889


/* clean up our tables and propagate them every this many seconds */
#define CLEANUPEVERY        120
int cleantime = CLEANUPEVERY;
int proptime = CLEANUPEVERY;


/* states of user table entries */
#define U_ALIVE     01
#define U_ZOMBIE    02
#define U_KILL      04

#define PNAMSIZ 32
struct uent {
  char uid[PNAMSIZ];            /* user id (must be unique) */
  char uname[PNAMSIZ];          /* user name (arbitrary text) */
  int state;                    /* user state */
  time_t logon;                 /* logon time at remote */
  time_t ttl;                   /* time to live of logon entry */
  time_t upd;                   /* last update on ttl */
  int gen;                      /* number of generations of prop */
  struct uent *next;
};



/* states of mudtable entries */
#define MUD_UP      001
#define MUD_PEER    002
#define MUD_GUEST   004
#define MUD_WIZ     010

struct mudent {
  struct in_addr maddr;         /* remote address */
  short mport;                  /* remote port */
  char *mapw;                   /* remote password */
  char *mnam;                   /* remote MUD name */
  char *txt;                    /* arbitrary text about MUD */
  time_t up;                    /* MUD's uptime */
  int ucnt;                     /* active (?) users */
  time_t ttl;                   /* time to live for MUD entry */
  time_t upd;                   /* last update on ttl */
  int flgs;                     /* flags */
  int gen;                      /* generation to PROPAGATE if peer */
  struct uent *usrs;
  struct mudent *next;
};
struct mudent *mt;
int havepeers = 0;

extern void process_stream(SOCKET fd);
extern void process_dgram(struct in_addr *who, char *buf);
extern void mud_free_ulist(struct mudent *m);
extern void mud_add_user(int ac, char *av[]);
extern void mud_zap_user(int ac, char *av[]);
extern void mud_add_entry(int ac, char *av[], int flg);
extern void mud_zap_entry(int ac, char *av[]);
extern void mud_def_newmud(char *buf);
extern void multicast_tables(SOCKET fd);
extern int loadmudtab(char *fil);

static void clean_tables (time_t now);


int dbgflg = 0;
int logflg = 0;

int main (int ac, char **av)
{
  struct sockaddr_in addr;
  struct timeval timo;
  fd_set redy;
  fd_set xcpt;
  time_t lastclean = (time_t) 0;
  time_t lastprop = (time_t) 0;
  time_t now;
  SOCKET dgramfd;
  SOCKET streamfd;
  char rbuf[512];
  SOCKET red;
  int alen;
  unsigned mydgport = DGRAMPORT;
  unsigned mystport = STREAMPORT;

  while ((alen = getopt (ac, av, "S:P:c:p:f:n:dlL:D")) != EOF) {
    switch (alen) {
    case 'S':
      mystport = atoi (optarg);
      break;

    case 'P':
      mydgport = atoi (optarg);
      break;

    case 'f':
      if (loadmudtab (optarg))
        exit (1);
      break;

    case 'n':
      myname = optarg;
      break;

    case 'c':
      cleantime = atoi (optarg);
      break;

    case 'p':
      proptime = atoi (optarg);
      break;

    case 'd':
      dbgflg++;
      break;

    case 'l':
      logflg++;
      break;

    case 'L':
      logfile = fopen (optarg, "wb");
      if (logfile == (FILE *) 0)
        perror (optarg);
      break;

    case 'D':
#ifdef  DAEMON
      (void) signal (SIGTERM, SIG_IGN);
      (void) signal (SIGHUP, SIG_IGN);
      (void) signal (SIGINT, SIG_IGN);
      (void) signal (SIGCHLD, SIG_IGN);
      (void) signal (SIGALRM, SIG_IGN);
      if (fork ())
        exit (0);
#ifdef  SETSID
      (void) setsid ();
#endif
#else
      fprintf (stderr, "cannot daemonize\n");
#endif
      break;

    default:
      fprintf (stderr, "usage: %s [-d] -f mudlist -n name\n", av[0]);
      exit (1);
    }
  }


#ifndef WIN32
  signal (SIGPIPE, SIG_IGN);
#endif

  if (havepeers && myname == (char *) 0) {
    fprintf (stderr, "cannot have server name unset and propagate\n");
    exit (1);
  }

  WIN32STARTUP

  /* set up datagram service */
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = INADDR_ANY;
  addr.sin_port = htons (mydgport);
  if ((dgramfd = socket (AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
    perror ("socket");
    WIN32CLEANUP
    exit (1);
  }
  if (bind (dgramfd, (struct sockaddr *) &addr, sizeof (addr))) {
    perror ("bind");
    WIN32CLEANUP
    exit (1);
  }


  /* set up stream service */
  addr.sin_port = htons (mystport);
  if ((streamfd = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
    perror ("socket");
    WIN32CLEANUP
    exit (1);
  }
  if (bind (streamfd, (struct sockaddr *) &addr, sizeof (addr))) {
    perror ("bind");
    WIN32CLEANUP
    exit (1);
  }
  if (listen (streamfd, 5) == -1) {
    perror ("listen");
    WIN32CLEANUP
    exit (1);
  }

  timo.tv_sec = cleantime > proptime ? cleantime : proptime;
  timo.tv_usec = 0;

  if ((dbgflg || logflg) && logfile != (FILE *) 0)
    fprintf (logfile, "server running: port %d/%d\n", mystport, mydgport);



/* main thang */
looptop:


  /* how often to purge expired entries */
  time (&now);
  if (now - lastclean > cleantime) {
    clean_tables (now);
    lastclean = now;
  }

  /* how often to propagate */
  if (now - lastprop > proptime) {
    if (havepeers)
      multicast_tables (dgramfd);
    lastprop = now;
  }


  FD_ZERO (&redy);
  FD_ZERO (&xcpt);
  FD_SET (streamfd, &redy);
  FD_SET (streamfd, &xcpt);
  FD_SET (dgramfd, &redy);
  FD_SET (dgramfd, &xcpt);


  if ((red = select (streamfd + 1, &redy, (fd_set *) 0, &xcpt, &timo)) == SOCKET_ERROR)
    if (GETERROR != EINTR) {
      perror ("select");
      WIN32CLEANUP
      exit (1);
    }


  /* yawn */
  if (red <= 0)
    goto looptop;


  if (FD_ISSET (dgramfd, &redy)) {
    alen = sizeof (addr);
    red = recvfrom (dgramfd, rbuf, sizeof (rbuf), 0, (struct sockaddr *)&addr, &alen);
    if (red > 0 && red < (int)sizeof (rbuf)) {
      rbuf[red] = '\0';
      process_dgram (&addr.sin_addr, rbuf);
    }
  }


  if (FD_ISSET (streamfd, &redy)) {
    red = accept (streamfd, (struct sockaddr *) 0, (int *) 0);
    if (red != INVALID_SOCKET) {
    #ifdef WIN32
      unsigned long flags = 1;

      if (ioctlsocket (red, FIONBIO, &flags)) {
    #else
      if (fcntl (red, F_SETFL, FNDELAY) == -1) {
    #endif
        shutdown (red, 2);
        goto looptop;
      }
      process_stream (red);
    }
  }

  if (FD_ISSET (streamfd, &xcpt) || FD_ISSET (dgramfd, &xcpt)) {
    if (logfile != (FILE *) 0)
      fprintf (logfile, "we are betrayed!!\n");
    WIN32CLEANUP
    exit (1);
  }


  goto looptop;
}





/* zip through our tables and kill anything with an expired time-to-live */
void clean_tables (now)
time_t now;
{
  struct mudent *m;
  struct uent *u;
  struct uent *p;

  if (dbgflg)
    printf ("cleaning deadwood out of tables\n");

  for (m = mt; m != (struct mudent *) 0; m = m->next) {
    if (!(m->flgs & MUD_UP))
      continue;

    /* has the whole MUD entry table expired? */
    if (now - m->upd > m->ttl) {
      if ((dbgflg || logflg) && logfile != (FILE *) 0)
        fprintf (logfile, "dead mud %s\n", m->mnam);
      m->flgs &= ~MUD_UP;
      mud_free_ulist (m);
      continue;
    }

    /* check for expired logins */
    u = p = m->usrs;
    while (u != (struct uent *) 0) {
      if (now - u->upd > u->ttl || (u->state == U_KILL)) {
        if ((dbgflg || logflg) && logfile != (FILE *) 0)
          fprintf (logfile, "dead user %s@%s\n", u->uid, m->mnam);
        if (u->state != U_KILL)
          m->ucnt--;

        if (u == m->usrs)
          m->usrs = u->next;
        else
          p->next = u->next;
        free (u);
      }
      p = u;
      u = u->next;
    }
  }
}





/* propagate out our tables to peer servers, if any */
void multicast_tables (SOCKET fd)
{
  struct sockaddr_in ad;
  struct uent *u;
  struct mudent *m;
  struct mudent *m2;
  char v[512];

  if (dbgflg)
    printf ("propagating tables\n");

  for (m = mt; m != (struct mudent *) 0; m = m->next) {
    if (!(m->flgs & MUD_PEER))
      continue;

    /* setup address */
    ad.sin_port = htons (m->mport);
    ad.sin_family = AF_INET;
    bcopy (&m->maddr, &ad.sin_addr, sizeof (ad.sin_addr));
    if (dbgflg)
      printf ("updating peer %s@%s, port %d\n", m->mnam,
        inet_ntoa (m->maddr), m->mport);


    for (m2 = mt; m2 != (struct mudent *) 0; m2 = m2->next) {

      /* EXCLUDE ! */
      if (m2 == m || m2->gen >= m->gen ||
        m2->flgs & MUD_PEER || !(m2->flgs & MUD_UP))
        continue;

      if (dbgflg)
        printf ("update mud %s->%s\n", m2->mnam, m->mnam);


      /* inform remote of the MUD */
      sprintf (v, "M\t%s\t%s\t%s\t%ld\t%d\t%s",
        myname, m->mapw, m2->mnam, m2->up,
        m2->gen + 1, m2->txt != (char *) 0 ? m2->txt : "");

      sendto (fd, v, strlen (v), 0, (struct sockaddr *)&ad, sizeof (ad));


      for (u = m2->usrs; u != (struct uent *) 0; u = u->next) {
        if (u->gen + 1 > m->gen || (u->state == U_KILL))
          continue;

        /*
           if the user is logged out send a byebye
           by propagating the zombie record.
           otherwise send a logged in record.
         */
        if (u->state == U_ZOMBIE) {
          if (dbgflg)
            printf ("deluser %s->%s\n", u->uid, m->mnam);
          sprintf (v, "Z\t%s\t%s\t%s\t%s", myname, m->mapw, m2->mnam, u->uid);
        } else {
          if (dbgflg)
            printf ("adduser %s->%s\n", u->uid, m->mnam);
          sprintf (v,
            "A\t%s\t%s\t%s\t%s\t%ld\t%d\t%s",
            myname, m->mapw, m2->mnam, u->uid,
            u->logon, u->gen + 1, u->uname);
        }
        sendto (fd, v, strlen (v), 0, (struct sockaddr *)&ad, sizeof (ad));
      }
    }
  }


  /* reap zombies - by marking them all as killable */
  for (m2 = mt; m2 != (struct mudent *) 0; m2 = m2->next)
    for (u = m2->usrs; u != (struct uent *) 0; u = u->next)
      if (u->state == U_ZOMBIE)
        u->state = U_KILL;
}






/* this is what most users will see, when they connect and ask who is on */
void process_stream (SOCKET fd)
{
  struct tm *tim;
  struct uent *u;
  struct mudent *m;
  char buf[512];
  char ibuf[64];
  int bl;

  if (send (fd, connectmsg,
      sizeof (connectmsg) - 1, 0) != (sizeof (connectmsg) - 1))
    goto done;

  for (m = mt; m != (struct mudent *) 0; m = m->next) {
    if (!(m->flgs & MUD_UP))
      continue;

    if (m->txt == (char *) 0)
      strcpy (ibuf, m->mnam);
    else
      sprintf (ibuf, "%.13s/%.22s", m->mnam, m->txt);

    tim = localtime (&m->up);

    /* hellish format */
    sprintf (buf,
      "\n%-35.35s %-3d user%c           up %d/%d/%d %2.2d:%2.2d\n",
      ibuf, m->ucnt, m->ucnt == 1 ? ' ' : 's',
      tim->tm_mon, tim->tm_mday, tim->tm_year, tim->tm_hour, tim->tm_min);

    bl = strlen (buf);
    if (send (fd, buf, bl, 0) != bl)
      goto done;

    if ((u = m->usrs) == (struct uent *) 0 || m->ucnt <= 0)
      continue;

    sprintf (buf, "%-35.35s %-17.17s      %s\n",
      "--name--", "--id--", "--login--");

    bl = strlen (buf);
    if (send (fd, buf, bl, 0) != bl)
      goto done;

    while (u != (struct uent *) 0) {
      if (u->state != U_ALIVE) {
        u = u->next;
        continue;
      }

      tim = localtime (&u->logon);
      sprintf (buf, "%-35.35s %-17.17s      %2.2d:%2.2d.%2.2d\n",
        u->uname[0] == '\0' ? u->uid : u->uname,
        u->uname[0] == '\0' ? "" : u->uid,
        tim->tm_hour, tim->tm_min, tim->tm_sec);

      bl = strlen (buf);
      if (send (fd, buf, bl, 0) != bl)
        goto done;
      u = u->next;
    }
  }
done:
  if (dbgflg)
    printf ("responded to users query\n");
  close (fd);
}





/* process and deal with a datagram from a remote MUD or server */
void process_dgram (struct in_addr *who, char *buf)
{
  struct mudent *m;
  struct uent *u;
  char *cp;
  char *av[12];
  int ac;
  int x;


  /* tokenize at tabs */
  av[ac = 0] = cp = buf;
  while (*cp != '\0') {
    if (*cp == '\t') {
      if (ac > 10)
        return;
      *cp++ = '\0';
      av[++ac] = cp;
      av[ac + 1] = (char *) 0;
    } else
      cp++;
  }


  /* minimum req: OP, mudname, password */
  if (ac < 3) {
    if ((dbgflg || logflg) && logfile != (FILE *) 0)
      fprintf (logfile, "malformed input: %s\n", inet_ntoa (*who));
    return;
  }


  /* authenticate */
  for (m = mt; m != (struct mudent *) 0; m = m->next) {
    if (m->flgs & MUD_GUEST)
      continue;
    if (m->mnam != (char *) 0 && !strcmp (m->mnam, av[1])) {
      if (bcmp (who, &m->maddr, sizeof (struct in_addr))) {
        if (logfile != (FILE *) 0)
          fprintf (logfile, "spoof %s from %s\n", av[1], inet_ntoa (*who));
        return;
      }
      break;
    }
  }


  /* unknown MUD */
  if (m == (struct mudent *) 0) {
    if (logfile != (FILE *) 0)
      fprintf (logfile, "probe from %s@%s", av[1], inet_ntoa (*who));
    if (logfile != (FILE *) 0 && ac > 2)
      fprintf (logfile, " (pass=%s)", av[2]);
    if (logfile != (FILE *) 0)
      fprintf (logfile, "\n");
    return;
  }


  /* bogey */
  if (strcmp (m->mapw, av[2])) {
    if (logfile != (FILE *) 0)
      fprintf (logfile, "bad passwd %s from %s@%s\n",
        av[2], av[1], inet_ntoa (*who));
    return;
  }


  /* nice to hear from you again - update remote's ttl */
  time (&m->upd);



  /*
     parameters passed are: operation, mudname, passwd
     since we have already used them by this point, we
     can now shift stuff around when we pass it to the
     actual operands themselves.
   */
  switch (*av[0]) {


    /* remote MUD declares it is alive and booted */
  case 'U':
    mud_add_entry (ac - 2, &av[3], 1);
    break;


    /* remote MUD existence update only (nondestructive) */
  case 'M':
    mud_add_entry (ac - 2, &av[3], 0);
    break;


    /* remote MUD declares it is down and out */
  case 'D':
    mud_zap_entry (ac - 2, &av[3]);
    break;


    /* add a new logged in user */
  case 'A':
    mud_add_user (ac - 2, &av[3]);
    break;


    /* zap an old logged in user */
  case 'Z':
    mud_zap_user (ac - 2, &av[3]);
    break;


    /* remote MUD declares it wants to define a new trusted MUD */
  case 'W':
    if (m->flgs & MUD_WIZ)
      mud_def_newmud (av[3]);
    break;


  default:
    return;
  }
}





/* read the MUD table of known servers and trusted MUDs */
int loadmudtab (char *fil)
{
  FILE *inf;
  char buf[BUFSIZ];

  if ((inf = fopen (fil, "rb")) == (FILE *) 0) {
    perror (fil);
    return (1);
  }

  while (fgets (buf, sizeof (buf), inf) != (char *) 0) {
    if (*buf == '#' || *buf == '\n')
      continue;
    mud_def_newmud (buf);
  }
  fclose (inf);
  return (0);
}




/* wipe the user list for a MUD */
void mud_free_ulist (struct mudent *m)
{
  struct uent *u;
  struct uent *n;

  if (dbgflg)
    printf ("clearing user list for %s\n", m->mnam);
  u = m->usrs;
  while (u != (struct uent *) 0) {
    n = u->next;
    if (dbgflg)
      printf ("clearing user %s@%s\n", u->uid, m->mnam);
    free (u);
    u = u->next;
  }
  m->usrs = (struct uent *) 0;
  m->ucnt = 0;
}





/* scan the MUD table */
struct mudent *getmudent (char *mud)
{
  struct mudent *ret;

  for (ret = mt; ret != (struct mudent *) 0; ret = ret->next)
    if (ret->mnam != (char *) 0 && !strcmp (ret->mnam, mud))
      return (ret);
  return ((struct mudent *) 0);
}





/*
add a user entry to a defined MUD if we can
parameters are: mudname username login-time propagation-generation
*/
void mud_add_user (int ac, char *av[])
{
  struct uent *u;
  struct mudent *m = NULL;
  int tgen;

  if (ac < 4 || (m = getmudent (av[0])) == (struct mudent *) 0) {
    if (dbgflg && ac > 3 && m == (struct mudent *) 0)
      printf ("unknown mud %s\n", av[0]);
    if (dbgflg && ac < 4)
      printf ("add user: bad arg count %d\n", ac);
    return;
  }

  if ((tgen = atoi (av[3])) > m->gen) {
    if (dbgflg)
      printf ("ignore gen %d %s@%s\n", tgen, av[1], m->mnam);
    return;
  }

  u = m->usrs;
  while (u != (struct uent *) 0) {
    if (!strcmp (u->uid, av[1])) {
      time (&u->upd);
      u->gen = tgen;
      if (u->state != U_ALIVE) {
        u->state = U_ALIVE;
        if (dbgflg)
          printf ("resurrect %s@%s\n", av[1], m->mnam);
        m->ucnt++;
      } else {
        if (dbgflg)
          printf ("refresh %s@%s\n", av[1], m->mnam);
      }
      return;
    }
    u = u->next;
  }

  u = (struct uent *) malloc (sizeof (struct uent));
  if (u == (struct uent *) 0)
    return;
  u->ttl = PLYTTL;
  u->upd = m->upd;
  u->state = U_ALIVE;
  u->logon = (time_t) atoi (av[2]);
  u->gen = tgen;
  strncpy (u->uid, av[1], PNAMSIZ - 1);

  u->uname[0] = '\0';
  if (ac == 5)
    strncpy (u->uname, av[4], PNAMSIZ - 1);

  u->next = m->usrs;
  m->usrs = u;
  m->ucnt++;
  if ((dbgflg || logflg) && logfile != (FILE *) 0)
    fprintf (logfile, "new entry %s@%s\n", av[1], m->mnam);
}





/*
zap a user
parameters are: mudname username
*/
void mud_zap_user (int ac, char *av[])
{
  struct mudent *m = NULL;
  struct uent *u;

  if (ac != 2 || (m = getmudent (av[0])) == (struct mudent *) 0) {
    if (dbgflg && ac == 2 && m == (struct mudent *) 0)
      printf ("unknown mud %s\n", av[0]);
    if (dbgflg && ac != 2)
      printf ("zap user: bad arg count %d\n", ac);
    return;
  }

  u = m->usrs;
  while (u != (struct uent *) 0) {
    if (u->state == U_ALIVE && !strcmp (u->uid, av[1])) {
      m->ucnt--;
      u->state = U_ZOMBIE;
      if ((dbgflg || logflg) && logfile != (FILE *) 0)
        fprintf (logfile, "logout %s@%s\n", av[1], m->mnam);
    }
    u = u->next;
  }
}





/*
define a new entry for a MUD, or mark one as up and well
parameters are: mudname uptime propagation-generation moretext
*/
void mud_add_entry (int ac, char *av[], int flg)
{
  struct mudent *m = NULL;
  int tgen;

  if (ac < 3 || (m = getmudent (av[0])) == (struct mudent *) 0) {
    if (ac < 3) {
      if (dbgflg)
        printf ("add entry: bad arg count %d\n", ac);
      return;
    }

    m = (struct mudent *) malloc (sizeof (struct mudent));
    if (m == (struct mudent *) 0) {
      perror ("malloc");
      return;
    }

    m->mapw = (char *) 0;
    m->mnam = malloc ((unsigned) (strlen (av[0]) + 1));
    if (m->mnam == (char *) 0) {
      perror ("malloc");
      return;
    }
    strcpy (m->mnam, av[0]);

    m->txt = (char *) 0;
    m->ucnt = 0;
    m->flgs = MUD_GUEST;
    m->usrs = (struct uent *) 0;
    m->next = mt;
    m->gen = atoi (av[2]);
    mt = m;
    if ((dbgflg || logflg) && logfile != (FILE *) 0)
      fprintf (logfile, "added mud table entry %s\n", m->mnam);
  } else {
    /* do not accept info that is too old */
    if ((tgen = atoi (av[2])) > m->gen) {
      if (dbgflg)
        printf ("ignored gen %d uptime %s\n", tgen, m->mnam);
      return;
    }
  }


  if (ac > 3) {
    /* replace name ? */
    if (m->txt != (char *) 0 && strcmp (m->txt, av[3])) {
      free (m->txt);
      m->txt = (char *) 0;
    }
    if (m->txt == (char *) 0) {
      m->txt = malloc ((unsigned) (strlen (av[3]) + 1));
      if (m->txt == (char *) 0) {
        perror ("malloc");
        return;
      }
      strcpy (m->txt, av[3]);
    }
  }

  m->flgs |= MUD_UP;
  m->ttl = MUDTTL;

  time (&m->upd);
  m->up = (time_t) atoi (av[1]);

  /* reset */
  if (flg)
    mud_free_ulist (m);

  if (dbgflg)
    printf ("mark mud %s up\n", m->mnam);
}





/*
mark an old entry for a MUD as deleted/down
parameters are: mudname
*/
void mud_zap_entry (int ac, char *av[])
{
  struct mudent *m = NULL;
  int tgen;

  if (ac != 1 || (m = getmudent (av[0])) == (struct mudent *) 0) {
    if (dbgflg && ac == 1 && m == (struct mudent *) 0)
      printf ("unknown mud %s\n", av[0]);
    if (dbgflg && ac != 1)
      printf ("zap entry: bad arg count %d\n", ac);
    return;
  }
  m->flgs &= ~MUD_UP;
  if ((dbgflg || logflg) && logfile != (FILE *) 0)
    fprintf (logfile, "mark mud %s down\n", m->mnam);
}



void mud_def_newmud (char *buf)
{
  struct mudent *mudp;
  struct hostent *hp;
  struct in_addr ad;
  short tmport;
  int tmpflg = 0;
  int tmpgen = 0;
  char *cp;
  char *xp;
  char *ho;
  char *nm;
  char *pw;

  cp = buf;

  /* scan out flags */
  while (*cp != '\0' && !isspace (*cp)) {
    switch (*cp) {

    case 'C':                  /* no-op */
      break;

    case 'W':
      tmpflg |= MUD_WIZ;
      break;

    case 'P':
      tmpflg |= MUD_PEER;
      havepeers++;
      break;

    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      tmpgen = *cp - '0';
      break;

    default:
      fprintf (stderr, "unknown flag %c\n", *cp);
      return;
    }
    cp++;
  }


  /* skip space */
  while (*cp != '\0' && isspace (*cp))
    cp++;


  /* scan out hostname */
  ho = cp;
  while (*cp != '\0' && !isspace (*cp))
    cp++;

  if (*cp == '\0') {
    fprintf (stderr, "malformed mud entry: %s\n", buf);
    return;
  }
  *cp++ = '\0';

  xp = ho;
  while (*xp != '\0' && (*xp == '.' || isdigit (*xp)))
    xp++;


  /* not all digits or dots */
  if (*xp != '\0') {
    if ((hp = gethostbyname (ho)) == (struct hostent *) 0) {
      fprintf (stderr, "unknown host %s\n", ho);
      return;
    }
    bcopy (hp->h_addr, &ad, hp->h_length);
  } else {
#ifdef WIN32
    unsigned long f;
#else
    struct in_addr f;
#endif

#ifdef WIN32
    if ((f = inet_addr (ho)) == INADDR_NONE) {
#else
    if (inet_aton (ho, &f) == 0) {
#endif
      fprintf (stderr, "cannot interpret %s\n", ho);
      return;
    }
    bcopy (&f, &ad, sizeof (f));
  }




  /* skip space */
  while (*cp != '\0' && isspace (*cp))
    cp++;

  /* scan out port # */
  xp = cp;
  while (*cp != '\0' && !isspace (*cp))
    cp++;

  if (*cp == '\0') {
    fprintf (stderr, "missing mud port: %s\n", ho);
    return;
  }
  *cp++ = '\0';
  tmport = atoi (xp);




  /* skip out spaces */
  while (isspace (*cp))
    cp++;

  /* get name */
  nm = cp;
  while (*cp != '\0' && !isspace (*cp))
    cp++;

  if (*cp == '\0' || *nm == '\0') {
    fprintf (stderr, "missing mud name: %s\n", ho);
    return;
  }
  *cp++ = '\0';





  /* skip out spaces */
  while (isspace (*cp))
    cp++;

  /* get password */
  pw = cp;
  while (*cp != '\0' && !isspace (*cp))
    cp++;

  if (*cp == '\0' || *pw == '\0') {
    fprintf (stderr, "missing mud password: %s\n", nm);
    return;
  }
  *cp++ = '\0';

  mudp = (struct mudent *) malloc (sizeof (struct mudent));
  if (mudp == (struct mudent *) 0) {
    perror ("malloc");
    return;
  }


  /* misc text */
  while (isspace (*cp))
    cp++;

  mudp->mapw = malloc ((unsigned) (strlen (pw) + 1));
  mudp->mnam = malloc ((unsigned) (strlen (nm) + 1));
  if (mudp->mapw == (char *) 0 || mudp->mnam == (char *) 0) {
    perror ("malloc");
    return;
  }
  strcpy (mudp->mapw, pw);
  strcpy (mudp->mnam, nm);

  mudp->txt = (char *) 0;
  if (*cp != '\0') {
    char *jnkp = cp;

    /* lose the newline */
    while (*jnkp != '\0' && *jnkp != '\n')
      jnkp++;
    if (*jnkp == '\n')
      *jnkp = '\0';

    /* save the text */
    mudp->txt = malloc ((unsigned) (strlen (cp) + 1));
    if (mudp->txt == (char *) 0) {
      perror ("malloc");
      return;
    }
    strcpy (mudp->txt, cp);
  }

  mudp->up = (time_t) 0;
  mudp->ucnt = 0;
  mudp->flgs = tmpflg;
  mudp->gen = tmpgen;
  mudp->usrs = (struct uent *) 0;
  mudp->mport = tmport;
  bcopy (&ad, &mudp->maddr, sizeof (ad));

  mudp->next = mt;
  mt = mudp;
  if (dbgflg)
    printf ("added mud table entry %s\n", mudp->mnam);
}