dmuck0.15-beta/docs/muf/
dmuck0.15-beta/game/
dmuck0.15-beta/game/logs/
dmuck0.15-beta/game/muf/
dmuck0.15-beta/game/muf/text/
#include "prims.h"
#include "config.h"
#include <stdio.h>    
#include <errno.h>    
#include <fcntl.h>    
#include <ctype.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

typedef struct muf_data
{
  int descriptor, connected, port;
  long connected_at, last_time;
  dbref uid;
  char *hostname;
  FILE *fd;
  struct muf_data *next, **prev;
} muf_data;

static int nmufs = 0;        /* For tracking MUF TCP sockets */
muf_data *muf_list = 0;      /* Init MUF TCP socket linked list */
#define MUF_READ 1
#define MUF_WRITE 0

/* private globals */
extern inst *p_oper1, *p_oper2, *p_oper3, *p_oper4;
extern int p_result;
extern int p_nargs;
extern dbref p_ref;
extern char p_buf[BUFFER_LEN];
static descriptor_data *dd;
extern int errno;

int desc_count()
{
  descriptor_data *d;
  int returnval = 0;
  for (d = descriptor_list; d; d = d->next, returnval++);
  return returnval;
}

descriptor_data *desc_num(int n)
{
  descriptor_data *return_desc;
  for (return_desc = descriptor_list; return_desc;
    return_desc = return_desc->next)
    if (return_desc->descriptor == n) break;
  return return_desc;
}

/****************************************
 * concount ( -- i ) - get number of connections to a MUCK
 ****************************************/
void prims_concount (__P_PROTO)
{
  if (*top >= STACK_SIZE) abort_interp("Stack Overflow!");
  p_result = desc_count();
  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
}

/****************************************
 * connections ( -- i1...iN N ) - get list of descriptors
 ****************************************/
void prims_connections (__P_PROTO)
{
  for (dd = descriptor_list, p_result = 0; dd; dd = dd->next)
  {
    if (*top >= STACK_SIZE) abort_interp("Stack Overflow!");
    if (!dd->connected || (!(FLAGS(dd->player) & DARK) || fr->wizard))
    {
      push(arg, top, PROG_INTEGER, MIPSCAST &(dd->descriptor));
      p_result++;
    }
  }
  if (*top >= STACK_SIZE) abort_interp("Stack Overflow!");
  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
}

/****************************************
 * condbref ( i -- d ) - get dbref of a certain descriptor
 ****************************************/
void prims_condbref (__P_PROTO)
{
  CHECKOP(1);
  p_oper1 = POP();
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  dd = desc_num(p_oper1->data.number);
  if (!dd) abort_interp("Illegal descriptor #.");
  p_ref = (dd->connected) ? dd->player : NOTHING;
  CLEAR(p_oper1);
  push(arg, top, PROG_OBJECT, MIPSCAST &p_ref);
}

/****************************************
 * conidle ( i -- i ) - get idle time for a connection
 ****************************************/
void prims_conidle (__P_PROTO)
{
  long now;
  CHECKOP(1);
  p_oper1 = POP();
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  p_result = p_oper1->data.number;
  dd = desc_num(p_result);
  if (!dd) abort_interp("Incorrect descriptor #.");
  (void) time (&now);
  p_result = now - dd->last_time;
  CLEAR(p_oper1);
  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
}

/****************************************
 * contime ( i -- i ) - get connection time for a descriptor
 ****************************************/
void prims_contime (__P_PROTO)
{
  long now;
  CHECKOP(1);
  p_oper1 = POP();
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  dd = desc_num(p_oper1->data.number);
  if (!dd) abort_interp("Incorrect descriptor #.");
  (void) time (&now);
  p_result = now - dd->connected_at;
  CLEAR(p_oper1);
  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
}

/****************************************
 * conhost ( i -- s ) - get connection site for a descriptor
 ****************************************/
void prims_conhost (__P_PROTO)
{
  CHECKOP(1);
  p_oper1 = POP();
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  dd = desc_num(p_oper1->data.number);
  if (!fr->wizard && (!dd->connected || !controls(fr->euid, dd->player)))
    abort_interp("Permission denied.");
  if (!dd) abort_interp("Invalid connection #.");
  CLEAR(p_oper1);
  push(arg, top, PROG_STRING,
    MIPSCAST dup_string(dd->hostname));
}

/****************************************
 * conboot ( i -- ) - boot off a connection
 ****************************************/
void prims_conboot (__P_PROTO)
{
  CHECKOP(1);
  p_oper1 = POP();
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  dd = desc_num(p_oper1->data.number);
  if (!fr->wizard && (!dd->connected || !controls(fr->euid, dd->player)))
    abort_interp("Permission denied.");
  if (!dd) abort_interp("Invalid connection #.");
  process_output(dd);
  shutdownsock(dd);
  CLEAR(p_oper1);
}

/****************************************
 * connotify ( i s -- ) - notify a connection
 ****************************************/
void prims_connotify (__P_PROTO)
{
  CHECKOP(2);
  p_oper2 = POP();
  p_oper1 = POP();
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  if (p_oper2->type != PROG_STRING) abort_interp("Argument not an string.");
  dd = desc_num(p_oper1->data.number);
  if (!fr->wizard && (!dd->connected || !controls(fr->euid, dd->player)))
    abort_interp("Permission denied.");
  if (!dd) abort_interp("Invalid connection #.");
  queue_string (dd, p_oper2->data.string);
  queue_write (dd, "\r\n", 2);
  CLEAR(p_oper1);
  CLEAR(p_oper2);
}

/****************************************
 * connnected? ( i -- i ) - returns 1 if descriptor is connected
 ****************************************/
void prims_connected (__P_PROTO)
{
  int res = 1;
  CHECKOP(1);
  p_oper1 = POP();
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument.");
  dd = desc_num(p_oper1->data.number);
  
  if (!dd) res = 0;

  CLEAR(p_oper1);
  push(arg, top, PROG_INTEGER, MIPSCAST &res);
}

void prims_awakep (__P_PROTO)
{
  CHECKOP(1);
  p_oper1 = POP();

  for (dd = descriptor_list, p_result = 0; dd; dd = dd->next)
  {
    if (*top >= STACK_SIZE) abort_interp("Stack Overflow!");
    if (dd->connected &&
      (dd->player == p_oper1->data.objref) &&
      (!(FLAGS(dd->player) & DARK) || fr->wizard))
      p_result++;
  }
  CLEAR(p_oper1);
  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
}

void prims_conlast (__P_PROTO)
{
 descriptor_data *tempd;
 p_result = -1;

  CHECKOP(1);
  p_oper1 = POP();
  if (!valid_object(p_oper1)) abort_interp("Invalid object.");
  if (Typeof(p_oper1->data.objref) != TYPE_PLAYER)
      abort_interp("Non-Player argument.");

    for(tempd = descriptor_list; tempd; tempd = tempd->next) 
      if(tempd->connected && tempd->player == p_oper1->data.objref) {
        p_result = tempd->descriptor;
        break;
      }

  CLEAR(p_oper1);
  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
}

void prims_pconnectors (__P_PROTO)
{
 descriptor_data *tempd;
 int i =0;
 p_result = -1;

  CHECKOP(1);
  p_oper1 = POP();
  if (!valid_object(p_oper1)) abort_interp("Invalid object.");
  if (Typeof(p_oper1->data.objref) != TYPE_PLAYER)
    abort_interp("Non-Player argument.");

  for(tempd = descriptor_list; tempd; tempd = tempd->next) {
   if(tempd->connected && tempd->player == p_oper1->data.objref) {
        p_result = tempd->descriptor;
        push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
        i++;
     }
   }

     CLEAR(p_oper1);
     push(arg, top, PROG_INTEGER, MIPSCAST &i);
}

muf_data *check_muf_desc(int i)
{
  muf_data *d;

  for (d = muf_list; d; d = d->next)  {
     if(d->descriptor == i)
       return d;
   }
  return NULL;
}

int close_muf(muf_data *d)
{
  int i;
          log_status("CLOSE MUF: descriptor %d\n", d->descriptor);
          i = close(d->descriptor) + 1;
          if(d->hostname) free(d->hostname);
          fclose(d->fd);
          *d->prev = d->next;
          if (d->next) d->next->prev = d->prev;
          if(d) free(d);
          nmufs--;
  return i;
}

int wread(int fd, int iobit)
{
  fd_set writebits, readbits;
  struct timeval timer;
  char garbage[1];
  int ret;

	timer.tv_sec =  0;
	timer.tv_usec = 100;
	FD_ZERO(&writebits);
	FD_ZERO(&readbits);
        if(iobit == MUF_WRITE)
           FD_SET(fd, &writebits);
        else if(iobit == MUF_READ)
           FD_SET(fd, &readbits);

        if (select(fd+1, &readbits, &writebits, (fd_set *)NULL, &timer) < 0)
        if (errno != EINTR) panic("Select failed in wread.");

            ret = recv(fd, garbage, 0, MSG_PEEK);   /* select() lies on */
            if (ret == -1 && errno != EINTR)        /* some machines    */
               return 0;

    if(iobit == MUF_WRITE) {
	if (FD_ISSET(fd, &writebits))
            return 1;
     }
     else if (iobit == MUF_READ) {
	if (FD_ISSET(fd, &readbits)) 
           return 1;
       }
   return 0;
}

int get_host_address(char *name, struct in_addr * addr)
{				/* Taken from Tinytalk version 117 */
  struct hostent *blob;
  union {			/* %#@!%!@%#!@ idiot who designed */
    long signed_thingy;		/* the inetaddr routine.... */
    unsigned long unsigned_thingy;
  } thingy;

  if (!*name) return (0);
  
  if ((*name >= '0') && (*name <= '9')) {	/* IP address. */
    addr->s_addr = inet_addr(name);
    thingy.unsigned_thingy = addr->s_addr;
    if (thingy.signed_thingy == -1) {
      return (0);
    }
  } else {			/* Host name. */
    blob = gethostbyname(name);

    if (blob == NULL) {
      return (0);
    }
    bcopy(blob->h_addr, (char *) addr, sizeof(struct in_addr));
  }
  return (1);			/* Success. */
}

void prims_socket (__P_PROTO)
{
  muf_data *d;
  int s;

   if(!fr->wizard) abort_interp("Permission denied.");

      s = socket(AF_INET, SOCK_STREAM, 0);
      if (s < 0)  {
         abort_interp("Socket could not be created.");
     }
      else {
         nmufs++;
         if(!(d = (muf_data *) malloc(sizeof(muf_data)))) 
                  panic("Out of memory in prims_socket.");
         make_nonblocking(s);
         d->descriptor = s;       /* Fill out the structure with info */
         d->connected_at = 0;
         d->port = 0;
         d->last_time = 0;
         d->uid = fr->euid;
         d->hostname = NULL;
         d->fd = fdopen(s, "r");
         if (muf_list) muf_list->prev = &d->next;
         d->next = muf_list;
         d->prev = &muf_list;
         muf_list = d;
         p_result = s;
    }
    push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
}

void prims_connect (__P_PROTO)
{
  struct in_addr host_address;
  struct sockaddr_in socket_address;
  muf_data *d;
  int err;

  CHECKOP(3);
  p_oper1 = POP();
  p_oper2 = POP();
  p_oper3 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper2->type != PROG_STRING) abort_interp("Non-string argument (2)");
  if (!p_oper2->data.string) abort_interp("NULL string argument (2)");
  if (p_oper1->type != PROG_INTEGER || p_oper3->type != PROG_INTEGER)
      abort_interp("Non integer argument");
  if(!(d = check_muf_desc(p_oper3->data.number)))
      abort_interp("Invalid socket.");
  if(p_oper1->data.number <= 0 ) abort_interp("Invalid port.");
  if (!get_host_address(p_oper2->data.string, &host_address)) 
      abort_interp("Invalid host address.");

      socket_address.sin_family = AF_INET;
      socket_address.sin_port = htons(p_oper1->data.number);
      bcopy((char *) &host_address, (char *) &socket_address.sin_addr, 
            sizeof(struct in_addr));
      err = connect(p_oper3->data.number, &socket_address, 
                    sizeof(struct sockaddr_in));

      if (err < 0 && errno != EINPROGRESS) {
          close_muf(d);
          p_result = 0;
	}
      else {
          d->connected_at = time(NULL);
          d->hostname = dup_string(p_oper2->data.string);
          d->port = p_oper1->data.number;
          p_result = 1;
       }

  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
  CLEAR(p_oper1);
  CLEAR(p_oper2);
  CLEAR(p_oper3);
}

void prims_close (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
  if((d = check_muf_desc(p_oper1->data.number))) {
          p_result = close_muf(d);
   }
  else
     p_result = -1;

  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
  CLEAR(p_oper1);
}

void prims_ready_write (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
  if(!(d = check_muf_desc(p_oper1->data.number)))
     p_result = -1;
  else  {
    p_result = wread(p_oper1->data.number, MUF_WRITE);
   }

  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
  CLEAR(p_oper1);
}

void prims_ready_read (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

   if(!fr->wizard) abort_interp("Permission denied.");
   if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
   if(!(d = check_muf_desc(p_oper1->data.number)))
      p_result = -1;
   else {
     p_result = wread(p_oper1->data.number, MUF_READ);
   }
 
   push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
   CLEAR(p_oper1);
}

void prims_socket_read (__P_PROTO)
{
  muf_data *d;
  CHECKOP(2);
  p_oper1 = POP();
  p_oper2 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument (2)");
  if (p_oper2->type != PROG_INTEGER) abort_interp("Non-integer argument (1)");
  if (p_oper1->data.number <= 0) abort_interp("Invalid number of bytes (2)");
  if(!(d = check_muf_desc(p_oper2->data.number)))  {
      abort_interp("Invalid socket");
  }
  else {
     if(!wread(p_oper2->data.number, MUF_READ))
         abort_interp("Socket not ready for read");
     if(p_oper1->data.number > BUFFER_LEN) 
        abort_interp("Buffer is too big!");

     for( p_result=0; p_result < BUFFER_LEN; p_result++)
          p_buf[p_result] = '\0';
 
      p_result=read(p_oper2->data.number, p_buf, p_oper1->data.number);

      if(p_result == -1) close_muf(d);
      else
         d->last_time = time(NULL);
    }
   push(arg, top, PROG_STRING, MIPSCAST &p_buf);
   push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
   CLEAR(p_oper1);
   CLEAR(p_oper2);
}

void prims_socket_write (__P_PROTO)
{
  muf_data *d;
  char tmp[BUFFER_LEN+1];
  char tmp2[BUFFER_LEN+1];
  char *ptr1, *ptr2;
  CHECKOP(2);
  p_oper1 = POP();
  p_oper2 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_STRING) abort_interp("Non-string argument (1)");
  if (!p_oper1->data.string )abort_interp("NULL string argument (2)");
  if (p_oper2->type != PROG_INTEGER) abort_interp("Non-integer argument (2)");
  if(!(d = check_muf_desc(p_oper2->data.number)))
      abort_interp("Invalid socket.");
  if(!wread(p_oper2->data.number, MUF_WRITE))
      abort_interp("Socket not ready for write");
 
  strncpy(tmp, p_oper1->data.string, strlen(p_oper1->data.string)+1);
  ptr2 = tmp;
  ptr1 = tmp2;
    while(*ptr2) {
      if(*ptr2 == '%') {
         ptr2++;
         if (*ptr2 == 'r')
            *ptr1++ = '\r';
         else if(*ptr2 == 'n')
            *ptr1++ = '\n';
         else
            *ptr1++ = *ptr2;
      } else 
         *ptr1++ = *ptr2;
      ptr2++;
    }
  *ptr1++ = '\0';

/*     p_result=write(p_oper2->data.number, tmp2, strlen(tmp2)); */
       p_result=write_data(p_oper2->data.number, tmp2, strlen(tmp2));
     if(p_result == -1 && (errno != EWOULDBLOCK)) 
        close_muf(d);
     else
        d->last_time = time(NULL);

  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
  CLEAR(p_oper1);
  CLEAR(p_oper2);
}

void prims_socket_last (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
  if(!(d = check_muf_desc(p_oper1->data.number)))
      p_result = -1;
  else {
     p_result = d->last_time;
   }

  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
  CLEAR(p_oper1);
}

void prims_socket_connected_at (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
  if(!(d = check_muf_desc(p_oper1->data.number)))
      p_result = -1;
  else {
     p_result = d->connected_at;
  }

  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
  CLEAR(p_oper1);
}

void prims_socket_connected (__P_PROTO)
{
  struct in_addr host_address;
  struct sockaddr_in socket_address;
  int err;
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
  if(!(d = check_muf_desc(p_oper1->data.number)))
       abort_interp("Invalid socket");

  if (!get_host_address(d->hostname, &host_address)) 
      abort_interp("Invalid host address.  THIS SHOULD NOT HAPPEN!");

      socket_address.sin_family = AF_INET;
      socket_address.sin_port = htons(d->port);
      bcopy((char *) &host_address, (char *) &socket_address.sin_addr, 
            sizeof(struct in_addr));
      err = connect(d->descriptor, &socket_address, sizeof(struct sockaddr_in));

      if(err < 0) {
         switch(errno)
         {
           case EALREADY:
           case EINPROGRESS:
           case EINTR:
             p_result = 0;
             break;
           case EISCONN:
             p_result = 1;
             break;
           default:
             close_muf(d);
             p_result = -1;
             break;
         }
      } else
           p_result = 1;

  push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
  CLEAR(p_oper1);
}

void prims_socket_host (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

  if(!fr->wizard) abort_interp("Permission denied.");
  if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
  if(!(d = check_muf_desc(p_oper1->data.number)))
      strcpy(p_buf, "");
  else {
      if(d->hostname)
         strcpy(p_buf, d->hostname);
      else
         strcpy(p_buf, "");
   }

   push(arg, top, PROG_STRING, MIPSCAST &p_buf);
   CLEAR(p_oper1);
}

void prims_socket_fgets (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

   if(!fr->wizard) abort_interp("Permission denied.");
   if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
   if(!(d = check_muf_desc(p_oper1->data.number)))
     abort_interp("Invalid descriptor.");

   if(!d->fd) abort_interp("Couldn't open stream.");
   if(!fgets(p_buf, sizeof(p_buf), d->fd))
       strcpy(p_buf, "");

   push(arg, top, PROG_STRING, MIPSCAST &p_buf);
}

void prims_is_socket (__P_PROTO)
{
  muf_data *d;
  CHECKOP(1);
  p_oper1 = POP();

   p_result = 0;
   if(!fr->wizard) abort_interp("Permission denied.");
   if (p_oper1->type != PROG_INTEGER) abort_interp("Non-integer argument");
   if((d = check_muf_desc(p_oper1->data.number)))
       p_result = 1;

   push(arg, top, PROG_INTEGER, MIPSCAST &p_result);
   CLEAR(p_oper1);
}

void prims_xevent_pop (__P_PROTO)
{
  CHECKOP(1);
  p_oper1 = POP();

#ifdef XEVENTS
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  dd = desc_num(p_oper1->data.number);
  if (!dd) abort_interp("Illegal descriptor #.");
  if (!dd->connected) abort_interp("Descriptor not connected.");
  if (!fr->wizard || !controls(fr->euid, dd->player))
     abort_interp("Permission denied.");
  if(dd->q && dd->q->comm) {
     strcpy(p_buf, dd->q->comm);
     free_queue(dd);
  }
  else
     strcpy(p_buf, "");
#else
  abort_interp("XEVENTS not enabled on this server.");
#endif

  CLEAR(p_oper1);
  push(arg, top, PROG_STRING, MIPSCAST dup_string(p_buf));
}

void prims_xevent_flush (__P_PROTO)
{
  CHECKOP(1);
  p_oper1 = POP();

#ifdef XEVENTS
  if (p_oper1->type != PROG_INTEGER) abort_interp("Argument not an integer.");
  dd = desc_num(p_oper1->data.number);
  if (!dd) abort_interp("Illegal descriptor #.");
  if (!dd->connected) abort_interp("Descriptor not connected.");
  if (!fr->wizard || !controls(fr->euid, dd->player))
     abort_interp("Permission denied.");
  if(dd->q) 
   {
     while(dd->q)
       free_queue(dd);
   }
#else
  abort_interp("XEVENTS not enabled on this server.");
#endif
}

int write_data(s, buffer, len)
int s;
char *buffer;
int len;
{
 int numwritten;

     while (len > 0) {
        if ((numwritten = write(s, buffer, len)) == -1)  {
           if(errno == EWOULDBLOCK) {
                numwritten = 0;
/*                wread(d->descriptor); */
            }
               else
                 return -1;   /* ACK!  Better be in a child! */
        }
        len -= numwritten;
        buffer += numwritten;
     }
   return numwritten;
}