pdirt/data/
pdirt/data/HELP/
pdirt/data/HELP/0/
pdirt/data/HELP/F/
pdirt/data/HELP/G/
pdirt/data/HELP/H/
pdirt/data/HELP/J/
pdirt/data/HELP/K/
pdirt/data/HELP/O/
pdirt/data/HELP/Q/
pdirt/data/HELP/R/
pdirt/data/HELP/U/
pdirt/data/HELP/V/
pdirt/data/HELP/Y/
pdirt/data/HELP/Z/
pdirt/data/MESSAGES/
pdirt/data/POWERINFO/
pdirt/data/WIZ_ZONES/
pdirt/drv/
pdirt/drv/bin/
pdirt/drv/compiler/converter/
pdirt/drv/compiler/libs/
pdirt/drv/compiler/scripts/
pdirt/drv/include/AberChat/
pdirt/drv/include/InterMud/
pdirt/drv/include/machine/
pdirt/drv/src/InterMud/
pdirt/drv/src/Players/
pdirt/drv/utils/UAFPort/
pdirt/drv/utils/dnsresolv/
pdirt/drv/utils/gdbm/
#undef LOG_INTERMUD
#include "kernel.h"
#include <sys/types.h>        /* include files for socket handling */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#include "log.h"
#include "uaf.h"
#include "pflags.h"
#include "sflags.h"
#include "sendsys.h"
#include "mobile.h"
#include "utils.h"
#include "timing.h"
#include "InterMud/intermud.h"
#include "InterMud/intermudP.h"

int imfd = -1;
int oobfd = -1;
int udpfd = -1;

char *rname;
char *raddr;
int rport;
int pport=0;
int tport=0;
int uport=0;
int mpasswd=0;
char *mudlib="0";
char *basemudlib="0";
char *driver="0";
char *mudType="0";
char *openStatus="0";
char *mudname="0";
char *adminEmail = "0";
int omid=0;
int ocid=0;
int numMuds=0;
char obuf[10240];


char *services[] = {
  "tell", "who", "finger", "locate", "channel", "news", "mail",
  "file", "smtp", "ftp", "nntp", "http", "rcp" , TABLE_END
};

pdarray mudlist = NULL;


imMapping Mappings[] = {
  { MESG_TELL,		"tell",			_imTell },
  { MESG_EMOTETO,	"emoteto",		_imEmote },
  { MESG_WHO_REQ,	"who-req",		_imWhoReq },
  { MESG_WHO_REPLY,	"who-reply",		_imWhoReply },
  { MESG_FINGER_REQ,	"finger-req",		_imFingerReq },
  { MESG_FINGER_REPLY,	"finger-reply",		_imFingerReply },
  { MESG_LOCATE_REQ,	"locate-req",		_imLocateReq },
  { MESG_LOCATE_REPLY,	"locate-reply",		_imLocateReply },
  { MESG_CHANLIST_REPLY,"chanlist-reply",	_imChanlReply },
  { MESG_CHANNEL_M,	"channel-m",		_imChanM },
  { MESG_CHANNEL_E,	"channel-e",		_imChanE },
  { MESG_CHANNEL_T,	"channel-t",		_imChanT },
  { MESG_CHANNEL_ADD,	"channel-add",		_imChanAdd },
  { MESG_CHANNEL_REMOVE,"channel-remove",	_imChanRem },
  { MESG_CHANNEL_ADMIN,	"channel-admin",	_imChanAdmin },
  { MESG_CHAN_WHO_REQ,	"channel-who-req",	_imChanWhoReq },
  { MESG_CHAN_WHO_REPLY,"channel-who-reply",	_imChanWhoReply},
  { MESG_CHANNEL_LISTEN,"channel-listen",	_imChanListen},
  { MESG_CHAN_USER_REQ,	"chan-user-req",	_imChanUserReq},
  { MESG_CHAN_USER_REPLY,"chan-user-reply",	_imChanUserReply },
  { MESG_NEWS_READ_REQ,	"news-read-req",	_imNewsReadReq },
  { MESG_NEWS_POST,	"news-post",		_imNewsPost },
  { MESG_NEWSGRP_LIST_REQ,"newsgrp-list-req",	_imNewsListReq },
  { MESG_MAIL,		"mail",			_imMail },
  { MESG_FILE,		"file",			_imFile },
  { MESG_ERROR,		"error",		_imError },
  { MESG_STARTUP_REQ_3,	"startup-req-3",	_imStartup3 },
  { MESG_STARTUP_REPLY,	"startup-reply",	_imStartupReply },
  { MESG_SHUTDOWN,	"shutdown",		_imShutdown },
  { MESG_MUDLIST,	"mudlist",		_imMudList },
  { MESG_UNKNOWN,	"unknown",		_imUnknown },
};

/***********************************************************
 *
 * mudSort
 *
 * Comparision function sed by the darray functionset and the 
 * qsort system call to sort the mud list.
 */

static int mudSort( const void *a, const void *b)
{
  mudInfo *mudA = * (mudInfo**)a;
  mudInfo *mudB = * (mudInfo**)b;

  return strcasecmp( mudA->name, mudB->name );
}

/***********************************************************
 * 
 * mudFind
 *
 * Comparision function used by the darray function to 
 * bsearch on the sorted mudlist.
 *
 */

static int mudFind( const void *key, const void *mud)
{
  char *find = *(char**)key;
  mudInfo *mudinfo = * (mudInfo**)mud;
  int minLen;

  if(mudinfo == NULL) return 1;

  minLen = ( strlen(find) < strlen(mudinfo->name) ) ?
               strlen(find) : strlen(mudinfo->name);

  return strncasecmp( find, mudinfo->name, minLen );
}

/***********************************************************
 * 
 * mudFind
 *
 * Comparision function used by the darray function to 
 * bsearch on the sorted mudlist.
 *
 */

static int mudStrictFind( const void *key, const void *mud)
{
  char *find = *(char**)key;
  mudInfo *mudinfo = * (mudInfo**)mud;

  if(mudinfo == NULL) return 1;

  return strcasecmp( find, mudinfo->name );
}

/*****************************************************
 *
 * __iConn
 *
 *  Create the connection to the intermud3 router and
 *  send the startup-req-3 packet to initiate the login
 *  sequence
 */

int __iConn(char *addr, int port)
{
  int sock;
  static struct hostent *host;
  static struct sockaddr_in server;
  static int done = 0;

  if(done == 0)
    {
      host = gethostbyname(addr);
      if(!host) return -1;
      
      server.sin_family = AF_INET;
      server.sin_port = htons(port);
      server.sin_addr = (*(struct in_addr*)host->h_addr_list[0]);
      done = 1;
    }
  
  sock = socket(AF_INET,SOCK_STREAM,0);
  if (connect(sock,(struct sockaddr *) &server,sizeof(server)) <0) {       
    close(sock);
    return -1;
  }

  obuf[0] = '\0';

  /* Message Header */
  sprintf(obuf,
	  "({\"startup-req-3\",5,\"%s\",0,\"%s\",0,%d,%d,%d,%d,%d,%d," /* everything up to ports. */
	  "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\","         /* mud info */
	  "([\"tell\":1,\"who\":1,\"finger\":1,\"locate\":1,]),0,})",
	  mudname,rname,mpasswd,omid,ocid,pport,tport,uport,
	  mudlib,basemudlib,driver,mudType,openStatus,adminEmail);
  imfd = sock;
  __iSend(obuf);

  return sock;
}

/*****************************************************
 *
 * uecopy
 *
 * strdup varient that strips out escaped characters from
 * a string.
 *
 */

char *uecopy(char *oldStr)
{
  /* Unescape quotes from a string as we copy it */
  char *temp = malloc(strlen(oldStr)+1);
  char *p,*q;
  for( p = oldStr, q = temp;
       *p; )
    {
      if(*p == '\\') p++; 
      *q++ = *p++;
    }
  *q = '\0';
  return temp;
}

/*****************************************************
 *
 * __iSend
 *
 *  Sends the length of the given string, then the string
 *  itself to the i3 router.
 *
 */

int __iSend(char *str)
{
  int sendVal;
  int len = strlen( str ) + 1;
  extern fd_set sockets_fds;

  if( imfd < 0 ) return -1;
  len = htonl( len );

  if( send( imfd, &len, sizeof( int ) , 0 ) > 0 )
    {
      if( ( sendVal = send( imfd, str, strlen( str ) + 1, 0 ) ) > 0 )
        {
#ifdef LOG_INTERMUD
          mudlog( "INTERMUD: Sent [%s]", str ); 
#endif
          return sendVal;
        } 
    }

  mudlog( "INTERMUD: ERROR Sending [%d][%s]", errno, str );
  close(imfd);
  FD_CLR(imfd,&sockets_fds);
  imfd = -1;
  return 1; 
}

/*****************************************************
 *
 * boot_intermud
 *
 * This function is called by the bootstrap function to
 * load configuration data from the intermud.conf file
 * to set up the intermud connection.
 *
 */

int boot_intermud(FILE *fp, char *fname)
{
/* char buf[256];*/
  FILE *nfp;

  /* Read in the config file... */
  while(!feof(fp))
    {
      char line[256],*opt,*value;
      fgets(line,255,fp);
      line[strlen(line)-1] = '\0';

      if(line[0] == '#') continue;
      opt = strtok(line,":"); if(!opt) continue;
      value = strtok(NULL,":"); if(!value) continue;
      
      if(EQ(opt,"mudname")) { mudname = COPY(value); }
      else if(EQ(opt,"routerip")) { raddr = COPY(value); }
      else if(EQ(opt,"routername")) { rname = COPY(value); }
      else if(EQ(opt,"routerport")) { rport = atoi(value); }
      else if(EQ(opt,"player port")) { pport = atoi(value); }
      else if(EQ(opt,"tcp port")) {tport = atoi(value); }
      else if(EQ(opt,"udp port")) {uport = atoi(value); }
      else if(EQ(opt,"mudlib")) {mudlib = COPY(value); }
      else if(EQ(opt,"basemudlib")) {basemudlib = COPY(value); }
      else if(EQ(opt,"driver")) {driver = COPY(value); }
      else if(EQ(opt,"email")) { adminEmail = COPY(value); }
      else if(EQ(opt,"mudType")) {mudType = COPY(value); }
      else if(EQ(opt,"openStatus")) {openStatus = COPY(value); }
    }


  mudlist = NULL;
  
  /*
   * when we support OOB tcp/udb, initialize them here.
   */

  if((nfp = fopen(INTERMUD_PASS,"r"))) {
    fscanf(nfp,"%d",&mpasswd);
    fclose(nfp);
  }
  if((nfp = fopen(MUDLIST_ID,"r"))) {
    fscanf(nfp,"%d",&omid);
    fclose(nfp);
  }
  if((nfp = fopen(CHANLIST_ID,"r"))) {
    fscanf(nfp,"%d",&ocid);
    fclose(nfp);
  }

  /*
   * build up the mudlist, if we can 
   */
  if((nfp = fopen(MUDLIST_FILE,"r"))) 
    {
      int numMuds;

      fscanf( nfp, "%d ", &numMuds );

      /* Init mudlist */
      if( mudlist == NULL )
        mudlist = newArray( numMuds, 10, mudSort, mudFind );

      while( !feof( nfp ) && numMuds-- )
        {
          char line[256];

	  mudInfo *newMud = (mudInfo*)malloc( sizeof( mudInfo ) );

          fgets(line,255,nfp); line[strlen(line)-1] = '\0';
          newMud->name = COPY(line);

          fgets(line,255,nfp); line[strlen(line)-1] = '\0';
          newMud->type = COPY(line);

          fgets(line,255,nfp); line[strlen(line)-1] = '\0';
          newMud->addr = COPY(line);

          fgets(line,255,nfp); line[strlen(line)-1] = '\0';
          newMud->playerPort = atoi(line);

          fgets(line,255,nfp); line[strlen(line)-1] = '\0';
          newMud->tcpPort = atoi(line);

          fgets(line,255,nfp); line[strlen(line)-1] = '\0';
          newMud->udpPort = atoi(line);

          fgets(line,255,nfp); line[strlen(line)-1] = '\0';
          newMud->isup = atoi(line);

          arrayAdd(mudlist, (void*)newMud );
        }

      fclose(nfp);
    }

  return 1;
}

/*****************************************************
 *
 * newList
 *
 *  Create a new list object.  This list represents an
 *  lpmud array, containing strings, ints, mappings, or
 *  other lists.
 */

listObject *newList(int numelements)
{
  listObject *newObj = (listObject*)malloc(sizeof(listObject));

  newObj->numElements = numelements;
  newObj->data = (char**)malloc(sizeof(char*) * numelements);
  newObj->type = (elemTypes*)malloc( numelements * sizeof(elemTypes) );
  return newObj;
}

/*****************************************************
 *
 * mappingObject
 *
 *  Create a new mapping object.  A mapping object is
 *  an associative list of form key : value, where key
 *  and value can both be one of all four lp types.
 *
 */

mappingObject *newMap(int numelements)
{
  mappingObject *map = (mappingObject*)malloc(sizeof(mappingObject));

  map->numElements = numelements;
  map->keyType = (elemTypes*)malloc(sizeof(elemTypes)*map->numElements);
  map->valueType = (elemTypes*)malloc(sizeof(elemTypes)*map->numElements);
  map->keys = (char**)malloc(sizeof(char*)*map->numElements);
  map->values = (char**)malloc(sizeof(char*)*map->numElements);
  return map;
}

/*****************************************************
 *
 * parseList2
 *
 *  Second stage of the i3 list parser.  This function 
 *  may be called recursively if the list we're parsing
 *  has another list inside of it.  Mail kender@hollyfeld.org
 *  if there are more detailed questions about the parsing 
 *  functions.
 *
 */

void parseList2(listObject *list)
{
#if 0
  int elementLength = 0; /* The length of this element */
  int elementID = 0;     /* The offset of this element */
#endif
  
  /* Ok, first, let's figure out just what thell it is we're looking at... */
  if( list->origString[0] == '}' )
    {
      /*
       * We've found the end of the list that we're looking at.
       * Start unwinding our parser
       */
      if(list->numElements)
	{
	  list->type = (elemTypes*)malloc( list->numElements * sizeof(elemTypes) );
	  list->data = (char**)malloc( list->numElements * sizeof(char*) );
	}
      return;
    }

  /* Check to see if we're looking at a string */
  if( list->origString[0] == '"' )
    {
      char *start = list->origString;
      char *p = start+1;
      
      /* Step to the next quote */
      for( ;*p && (*p != '"'); p++ )
	if(*p == '\\') p++; /* Step past the next char, don't care what it is now */
      
      if(*p)
	{
	  int myIndex = list->numElements++;
	  *p = '\0';  /* mark off the end of this string */
	  
	  /* We have a string! */
	  list->origString = ( *(p+1) == ',') ? (p+2) : (p+1);

	  /* Continue parsing down the list */
	  parseList2(list);

	  /* Fill in the data */
	  list->type[myIndex] = TYPE_STRING;
	  list->data[myIndex] = uecopy(start+1);
	}
    }

  /* Check to see if we're a number */
  else if( isdigit( list->origString[0] ) || ( list->origString[0] == '-' ) )
    {
      int myIndex = list->numElements++;
      char *p,*start = list->origString;

      for( p = start; (*p != ',') && (*p != '}'); p++)
	if(*p == '\\') p++;

      /* ok, p now points to the end of the number. */
      if(*p == ',') *p++ = '\0';
      list->origString = p;

      parseList2(list);

      list->type[myIndex] = TYPE_INT;
      list->data[myIndex] = (char*)atoi(start);
    }
  
  /* Check to see if we're looking at a mapping or a list... */
  else if( list->origString[0] == '(' )
    {
      /* We have a mapping or a list, pick one.. */
      if( list->origString[1] == '[' )
	{
	  /* We have a mapping... */
	  char *start = list->origString;
	  char *p = start;
	  int myIndex = list->numElements++;

	  mappingObject *map = parseMapping(p);
	  if(map) p = map->origString;
	  
	  /* list->origString now points to the closing */
	  /* p now points to the closing ]. */
	  if( *(p + 2) == ',' ) list->origString = (p+3);
	  else list->origString = (p+2);

	  /* Continue parsing the list */
	  parseList2(list);
	  
	  list->type[myIndex] = TYPE_MAPPING;
	  list->data[myIndex] = (char*)map;
	}
      else if( list->origString[1] == '{' )
	{
	  /* We have a list... */
	  char *start = list->origString;
	  char *p = start;
	  int myIndex = list->numElements++;

	  listObject *newList = parseList(p);
	  if(newList) p = newList->origString;
	  /* list->origString now points to the closing */
	  /* p now points to the closing ]. */
	  if( *(p + 2) == ',' ) list->origString = (p+3);
	  else list->origString = (p+2);

	  /* Continue parsing the list */
	  parseList2(list);
	  
	  list->type[myIndex] = TYPE_LIST;
	  list->data[myIndex] = (char*)newList;
	}
    }
}

/*****************************************************
 *
 * parseList
 *
 *  This is the first stage in parsing a list, it creates
 *  the lsit object, initializes it, and calls parseList2
 *  to start the parsing process.
 *
 */

listObject *parseList(char *line)
{
  listObject *list;
  
  /* This isn't a list! */
  if(strncmp(line,"({",2)) return NULL;

  list = (listObject*)malloc(sizeof(listObject));

  list->origString = line+2;
  list->numElements = 0;
  list->type = NULL;
  list->data = NULL;

  parseList2(list);

  return list;
}

/*****************************************************
 *
 * parseMap2
 *
 *  This is the second stage in a mapping parsing.  This
 *  function works almost like the parseList2 function,
 *  with the exception that it parses out both keys and
 *  values, instead of just elements.  Note: unlike full
 *  lp mappings, keys must be strings or ints.  We don't
 *  support lists or other mappings as keys.
 *
 */

void parseMap2(mappingObject *map)
{
  
  /* First, are we at the end of the mapping? */
  if(map->origString[0] == ']')
    {
      map->keyType = (elemTypes*)malloc(sizeof(elemTypes)*map->numElements);
      map->valueType = (elemTypes*)malloc(sizeof(elemTypes)*map->numElements);
      map->keys = (char**)malloc(sizeof(char*)*map->numElements);
      map->values = (char**)malloc(sizeof(char*)*map->numElements);
    }
  else
    {
      /* Ok, we weren't at the end of the mapping, then we are
       * at the beginning of a key/value pair.  Find the next
       * key.
       */
      char *p = map->origString;
      char *key = map->origString;
      elemTypes keyType = TYPE_STRING;
      int myIndex;

      if(*p=='"')
	{
	  key++;
	  for(p = key;*p && *p != '"'; p++)
	    if(*p == '\\') p++;
	  
	  if(!(*p))
	    {
	      /* We have no key!  whoops! */
	      return;
	    }
	  
	  *p++ = '\0'; /* p now points to the : */
          key = uecopy(key);
	}
      else if( isdigit(*p) )
	{
	  key = (char*)atoi(p);
	  while(*p != ':') p++;
	  keyType = TYPE_INT;
	}
      p++;         /* p now points to the first value past the colon */
      myIndex = map->numElements++;

      /*
       * Now that we have the key, we need the element info.
       */
      if(*p == '"') /* We found a string */
	{
	  char *start = ++p;

	  /* Step to the next quote */
	  for( ;*p && (*p != '"'); p++)
	    if(*p == '\\')p++;

	  if(*p)
	    {
	      *p = '\0';
	      map->origString = ( *(p+1) == ',') ? (p+2) : (p+1);

	      parseMap2(map);
	      map->keyType[myIndex] = keyType;
	      map->valueType[myIndex] = TYPE_STRING;
	      map->keys[myIndex] = key;
	      map->values[myIndex] = uecopy(start);
	      return;
	    }
	}
      else if ( ( isdigit (*p ) ) || ( *p == '-' ) )
	{
	  char *start = p;
	  for( ; (*p != ',') && (*p != ']'); p++)
	    if(*p == '\\') p++;

	  /* Ok, p now points to the end of the number. */
	  if(*p == ',') *p++ = '\0';
	  map->origString = p;

	  parseMap2(map);
	  map->keyType[myIndex] = keyType;
	  map->valueType[myIndex] = TYPE_INT;
	  map->keys[myIndex] = key;
	  map->values[myIndex] = (char*)atoi(start);
	  return;
	}
      else if( *p == '(' )
	{
	  if ( *(p+1) == '[' )
	    {
/*	      char *start = p;*/

	      mappingObject *newMap = parseMapping(p);
	      if(newMap) p = newMap->origString;

	      if( *(p + 2) == ',') map->origString = (p+3);
	      else map->origString = (p+2);

	      parseMap2(map);
	      map->keyType[myIndex] = keyType;
	      map->valueType[myIndex] = TYPE_MAPPING;
	      map->keys[myIndex] = key;
	      map->values[myIndex] = (char*)newMap;
	      return;
	    }
	  else if( *(p+1) == '{' )
	    {
/*	      char *start = p;*/

	      listObject *newList = parseList(p);
	      if(newList) p = newList->origString;

	      if( *(p + 2) == ',') map->origString = (p+3);
	      else map->origString = (p+2);

	      parseMap2(map);
	      
	      map->keyType[myIndex] = keyType;
	      map->valueType[myIndex] = TYPE_LIST;
	      map->keys[myIndex] = key;
	      map->values[myIndex] = (char*)newList;
	      return;
	    }
	}
    }
  
}

/*****************************************************
 *
 * parseMapping
 *
 *  This function is the first stage in the map parsing.
 *  It creates a mapping object, and starts the processing
 *  with parseMap2.
 *
 */

mappingObject *parseMapping(char *line)
{
/*  int depth;*/
  mappingObject *map;
/*  char *p = line+2;*/

  if(strncmp(line,"([",2)) return NULL;

  map = (mappingObject*)malloc( sizeof(mappingObject) );
  map->origString = line+2;
  map->numElements = 0;
  map->keyType = NULL;
  map->valueType = NULL;
  map->keys = NULL;
  map->values = NULL;

  parseMap2(map); 

  return map;
}

/*****************************************************
 *
 * destroyList
 *
 *  This function will destory a list object, recursively
 *  destroying lists and maps that are in the list.
 *
 */

void destroyList(listObject *list)
{
  int x;

  if(!list) return;
  for( x = 0; x < list->numElements; x++ )
    {
      if(list->type[x] == TYPE_LIST)
	destroyList( (listObject*) list->data[x] );
      else if( list->type[x] == TYPE_MAPPING )
	destroyMapping( (mappingObject*) list->data[x] );
      else if( list->type[x] == TYPE_STRING )
        if( list->data[x] ) free(list->data[x]);
    }
  free(list->data);
  free(list->type);
  free(list);
}

/*****************************************************
 *
 * destroyMapping
 *
 *  This function will destroy a mapping object, recursively
 *  destroying lists and maps that are in the mapping objects.
 *
 */

void destroyMapping(mappingObject *map)
{
  int x;
  
  if(!map) return;
  for(x = 0; x < map->numElements; x++)
    {
      switch(map->keyType[x])
	{
	case TYPE_LIST: destroyList((listObject*)map->keys[x]); break;
	case TYPE_MAPPING: destroyMapping((mappingObject*)map->keys[x]); break;
        case TYPE_STRING: if(map->keys[x]) free(map->keys[x]); break;
        default: break;
	}
      switch(map->valueType[x])
	{
	case TYPE_LIST: destroyList((listObject*)map->values[x]); break;
	case TYPE_MAPPING: destroyMapping((mappingObject*)map->values[x]); break;
        case TYPE_STRING: if(map->values[x]) free(map->values[x]); break;
	default: break;
	}
    }
  free(map->keys);
  free(map->values);
  free(map->keyType);
  free(map->valueType);
  free(map);
}
    
/*****************************************************
 *
 * buildImPacket2
 *
 *  This function will build an intermud packet from
 *  a mapping object, turning it into a mudmode compatable
 *  string.
 *
 */

int buildImPacket2(mappingObject *map,char *str)
{
  int x;
  char temp[256];

  strcat(str,"([");
  for( x = 0; x < map->numElements; x++)
    {
      switch(map->keyType[x])
        {
          case TYPE_STRING: 
	    strcat(str,"\"");
            strcat(str,map->keys[x]);
	    strcat(str,"\"");
 	    break;
	  case TYPE_INT:
	    sprintf(temp,"%d",(int)map->keys[x]);
	    strcat(str,temp);
	    break;
 	  case TYPE_LIST:
	    buildImPacket((listObject*)map->keys[x],str);
	    break;
	  case TYPE_MAPPING:
	    buildImPacket2((mappingObject*)map->keys[x],str);
            break;
	  default: break;
        }
      strcat(str,":");
      switch(map->valueType[x])
        {
          case TYPE_STRING: 
	    strcat(str,"\"");
            strcat(str,map->values[x]);
	    strcat(str,"\"");
 	    break;
	  case TYPE_INT:
	    sprintf(temp,"%d",(int)map->values[x]);
	    strcat(str,temp);
	    break;
 	  case TYPE_LIST:
	    buildImPacket((listObject*)map->values[x],str);
	    break;
	  case TYPE_MAPPING:
	    buildImPacket2((mappingObject*)map->values[x],str);
            break;
	  default: break;
        }
      strcat(str,",");
    }
  strcat(str,"])");
  return 1;
}

/*****************************************************
 *
 * buildImPacket
 * 
 *  Builds an intermud packet from a list object.
 *
 */

int buildImPacket(listObject *list,char *str)
{
  int x;
  char temp[256];

  strcat(str,"({");
  for( x = 0; x < list->numElements; x++)
    {
      switch(list->type[x])
        {
          case TYPE_STRING: 
	    strcat(str,"\"");
            strcat(str,list->data[x]);
	    strcat(str,"\"");
 	    break;
	  case TYPE_INT:
	    sprintf(temp,"%d",(int)list->data[x]);
	    strcat(str,temp);
	    break;
 	  case TYPE_LIST:
	    buildImPacket((listObject*)list->data[x],str);
	    break;
	  case TYPE_MAPPING:
	    buildImPacket2((mappingObject*)list->data[x],str);
            break;
	  default: break;
 	}
      strcat(str,",");
    }
  strcat(str,"})");
  return 1;
}

static int packetLen = 0;
static int tlen = 0;
static char *rbuf = NULL;

/*******************************************************
 *
 * imPacket
 *
 *   This function will read a packet in off of the intermud
 *   file descriptor.  It currently assumes that there will 
 *   be only one file descriptor.  This will change in the near
 *   future when OOB support is put in.  Once it reads the data
 *   in it will break out the packet into a listObject, and the
 *   dispatch the function that handles the given message type.
 *
 */
int imPacket(int fd)
{
  char *type,*orMud,*orUser,*tMud,*tUser;
  int x,ttl;
  listObject *packet;
  int t;
  
  if( packetLen == 0 ) /* We're not in the middle of reading a packet */
    {
      /*
       * get the length of the imud packet
       */
      t = read(fd,&packetLen,sizeof(int));

      if (t <= 0)
      {   mudlog("INTERMUD: Empty Packets or network error, closing connection");
          close(imfd);
          return -1;
      }

      packetLen = ntohl(packetLen);
      tlen = 0;

      if(packetLen > 0)
        {
#ifdef LOG_INTERMUD
          mudlog("DEBUG: Packet length is [%d]\n",packetLen); 
#endif
          rbuf = malloc(packetLen);
        }
      else return 0;
    }

  do {
    int x;
    x = read(fd,rbuf+tlen,packetLen-tlen);
    if( x <= 0 ) return 0;
#ifdef LOG_INTERMUD
    mudlog("INTERMUD: ping [%d:%d][%s]",tlen,packetLen,rbuf); 
#endif
    tlen += x;
  } while (tlen < packetLen);

#ifdef LOG_INTERMUD      
  mudlog("INTERMUD: recieved [%d:%d][%s]",tlen,packetLen,rbuf); 
#endif
      
  packet = parseList(rbuf);
      
  if(packet) 
    {
      type = packet->data[0];
      ttl = (int)packet->data[1];
      orMud = packet->data[2];
      orUser = packet->data[3];
      tMud = packet->data[4];
      tUser = packet->data[5];
          
      for( x = 0; Mappings[x].type != MESG_UNKNOWN ; x++ )
        {
          if(EQ(type,Mappings[x].name))
            {
              Mappings[x].handler(type,ttl,orMud,orUser,
    			          tMud,tUser,packet);
              break;
            }
        }     
      destroyList(packet);
      free(rbuf);
    }
  rbuf = NULL;
  packetLen = 0;
  return 0;
}

/***********************************************************\
 *
 * Intermud Message Handlers
 *
 * These functions will handle the parsing of individual
 * intermud messages.
\***********************************************************/ 

/*
 * _imStartupReply
 *
 * This function handles an i3 startup
 * reply packet.
 */
DECLARE_HANDLER(_imStartupReply )
{
  if( packet->type[6] == TYPE_LIST )
    {
      mudlog("INTERMUD: Startup reply router list recieved.");
    }
  if( packet->type[7] == TYPE_INT )
    {
      FILE *nfp;
      
      mudlog("INTERMUD: Mud password recieved.");
      if((nfp = fopen(INTERMUD_PASS,"w")))
	{
	  fprintf(nfp,"%d",(int)packet->data[7]);
	  fclose(nfp);
	}
    }
  mudlog("INTERMUD: Successful connection.");
}

/*
 * _imMudList - this command will send in
 *              the intermud mud list.
 */

DECLARE_HANDLER(_imMudList )
{
  FILE *nfp;
  if( packet->type[6] == TYPE_INT )
    {
#ifdef LOG_INTERMUD      
      mudlog("INTERMUD: Mudlist ID %d recieved.",(int)packet->data[6]); 
#endif
      if((nfp = fopen(MUDLIST_ID,"w")))
	{
	  omid = (int)packet->data[6];
	  fprintf(nfp,"%d",omid);
	  fclose(nfp);
	}
    }
  if( packet->type[7] == TYPE_MAPPING)
    {
      mappingObject *mudMap = (mappingObject*)packet->data[7];
      int x;
#ifdef LOG_INTERMUD      
       mudlog("INTERMUD: Mudlist mapping recieved, filling hash table."); 
#endif

      /*
       * init hash table here.
       */
      if(mudlist == NULL)
	mudlist = newArray( 10, 10, mudSort, mudFind);

      /*
       * For each mud that came in the list
       */
      for( x = 0; x < mudMap->numElements ; x++)
	{
	    {
	      char *name = COPY(mudMap->keys[x]);
	      char *p;
	      mudInfo *oldMud;
	      
	      for(p=name;*p;p++) *p = tolower(*p);
		  
	      if ( mudMap->valueType[x] == TYPE_LIST)
		{
		  listObject *mudItems = (listObject*)mudMap->values[x];
		  mudInfo *newMud;

		  oldMud = NULL;
		  if ((oldMud = (mudInfo*)arrayLFindWith(mudlist,(void*) name , mudStrictFind )) )
		    {
		      newMud = oldMud;
		      free(oldMud->name);
		      if(oldMud->type) free(oldMud->type);
		      if(oldMud->addr) free(oldMud->addr);
		    }
		  else
		    {
		      numMuds++;
		      newMud = (mudInfo *)malloc(sizeof(mudInfo));
		    }
		  
		  newMud->name = COPY(mudMap->keys[x]);
		  newMud->type = NCOPY(mudItems->data[8]);
		  newMud->addr = NCOPY(mudItems->data[1]);
		  newMud->playerPort = (int)mudItems->data[2];
		  newMud->tcpPort = (int)mudItems->data[3];
		  newMud->udpPort = (int)mudItems->data[4];
		  newMud->isup = ( (int)mudItems->data[0] ) == -1;
		  
		  /*
		   * see if this mud is in our hash table already
		   */
		  if(!oldMud)
                    {
		      arrayAdd(mudlist, (void*) newMud );
                    }
		}
	      else if ( mudMap->valueType[x] == TYPE_INT )
		{
/* mudInfo *oldMud;*/
		  /* We won't delete muds just yet :) 
		  if ( oldMud = (mudInfo*)arrayFind(mudlist,(void*)name) )
		    {
		      arrayRemove(mudlist, (void*)oldMud );
		      numMuds--;
		      free(oldMud->name);
		      if(oldMud->type) free(oldMud->type);
		      if(oldMud->addr) free(oldMud->addr);
		      free(oldMud);
		    }
                  */
		}
	    }

	}
      /* 
       * Write out updated mudlist file
       * and id
       */
      arraySort(mudlist);

      if((nfp = fopen( MUDLIST_FILE, "w" )))
        {
          int loop;

          fprintf(nfp,"%d\n", arrayGetSize( mudlist ) );

          for( loop = 0; loop < arrayGetSize( mudlist ); loop++ )
            {
              mudInfo *mud = arrayGet( mudlist, loop );
              if(!mud) continue;
              fprintf(nfp,"%s\n%s\n%s\n%d\n%d\n%d\n%d\n", 
                      mud->name,mud->type,mud->addr,
                      mud->playerPort,mud->tcpPort,mud->udpPort,mud->isup);
            }
          fclose(nfp);
        }
    }
}

/*******************************************************
 *
 * _imTell
 *
 *  packet handler to handle an intermud3 tell packet
 *  This function will try to find the user, and if they
 *  are available to intermud, will send the message to that user.
 *
 */
DECLARE_HANDLER(_imTell )
{
  if(packet->type[5] == TYPE_STRING)
    {
      int target;

      if( (target = fpbns(packet->data[5])) == -1)
	{
	  /* Bad player, do something here! */
	  return;
	}

      if( !ptstflg( target, PFL_INTERMUD ) )
        {
	  /* User can't access intermud, fail silently */
	  return;
        }
   
      if( ststflg( target, SFL_I_INVIS ) )
        {
 	  /* Target is ignoring intermud, fail silently */
	  return;
	}

      if(packet->type[7] == TYPE_STRING)
        {
	  sendf(target,"&+m%s&+M@&+m%s &*tells you &+B'%s'&*\n",
	        packet->data[6],
	        packet->data[2],
	        packet->data[7]);
        }
    }
}

/*******************************************************
 *
 * _imEmote
 *
 *   This function is the packet handler for the emoteto 
 *   packet type.
 *
 */
DECLARE_HANDLER(_imEmote )
{
  if(packet->type[5] == TYPE_STRING)
    {
      int target;
      char *message,*mud,*name,*q,*p,outbuf[2048];

      message = packet->data[7];
      mud = packet->data[2];
      name = packet->data[6];
      if(!message) message = "";
      if(!mud) mud = "";
      if(!name) name = "";
      
      if( (target = fpbns(packet->data[5])) == -1)
	{
	  /* Bad player, do something here! */
	  return;
	}
      if( !ptstflg( target, PFL_INTERMUD ) )
        {
	  /* User can't access intermud, fail silently */
	  return;
        }
   
      if( ststflg( target, SFL_I_INVIS ) )
        {
 	  /* Target is ignoring intermud, fail silently */
	  return;
	}

  /*    for(p = message, q = outbuf; *p; *p++,*q++)*/
      for(p = message, q = outbuf; *p; p++,q++)
	{
	  if(*p == '$')
            {
	      if(*(p+1) == 'N')
	        {
	  	  char *r;
		
		  for(r=name;*r;) *q++ = *r++;
		  *q++ = '@';
		  for(r=mud;*r;) *q++ = *r++;
		  p++;
		  q--;
		  continue;
	      }
            }
	  *q = *p;
	}
      *q = '\0';
      sendf(target,"%s\n",outbuf);

    }
}

/*******************************************************
 *
 * _imWhoReq
 *
 *  This function handles the building of a who list in
 *  respones to a who-request packet, and sends out a 
 *  who-reply packet in response.
 *
 */
DECLARE_HANDLER(_imWhoReq ) 
{
  listObject *list = newList(max_players);

  int i,num;

  sprintf(obuf,"({\"who-reply\",5,\"%s\",0,\"%s\",\"%s\",",
          mudname,
	  packet->data[2],
          packet->data[3]);
  for(i = 0, num = 0; i < max_players; i++)
    {
      listObject *thisPlayer;

      if ( EMPTY(pname(i))
        || !players[i].iamon
        || pstr(i) < 0
        || !ptstflg(i,PFL_INTERMUD) /* Can't access, can't be seen */
        || ststflg( i, SFL_I_INVIS ) /* hiding from intermud? */
        || pvis(i) > 0 ) { continue; }

     /* num;*/
      thisPlayer = newList(3);
      thisPlayer->type[0] = TYPE_STRING;
      thisPlayer->data[0] = COPY(pname(i));
      thisPlayer->type[1] = TYPE_INT;
      thisPlayer->data[1] = (long)(time(0) -players[i].last_command);
      thisPlayer->type[2] = TYPE_INT;
      thisPlayer->data[2] = (char*)0;
      list->type[num] = TYPE_LIST;
      list->data[num++] = (char*)thisPlayer;
    }
  list->numElements = num;
  buildImPacket(list,obuf);
  destroyList(list);
  strcat(obuf,",})");
  __iSend(obuf);
}

/*******************************************************
 *
 * _imWhoReply
 *
 *  This function handles a who-reply that comes in from another
 *  mud, it will build hte mudlist and send it to the user.
 *
 */

DECLARE_HANDLER(_imWhoReply ) 
{
  if(packet->type[5] == TYPE_STRING)
    {
      int target,x;
      char *mud;
      listObject *who;

      mud = packet->data[2];
      if(!mud) mud = "";

      if( (target = fpbns(packet->data[5])) == -1)
	{
	  /* Bad player, do something here! */
	  return;
	}
     
      who = (listObject*)packet->data[6];
      sendf(target,"Players active on %s\n",mud);
      sendf(target,"%-15.15s %-10.10s %s\n","Name","Idle For","Misc");
      sendf(target,"----------------------------------------------------\n");
      for(x = 0; x < who->numElements; x++)
        {
	  listObject *person = (listObject*)who->data[x];
          char *name = person->data[0];
          int  idle = (int)person->data[1]; 
          char idleStr[256];
	  char *misc = person->data[2];
          int  hours = idle/3600;  /* How many hours idle... */
          int  mins  = (idle - (hours * 3600)) / 60;
          int  secs  = idle - (hours * 3600) - (mins * 60);
   
	  if(!name) continue;
          sprintf(idleStr,"%03d:%02d:%02d",hours,mins,secs);
	  sendf(target,"%-15.15s %-10.10s %s\n",
                name,idleStr,(misc)?misc:"");
        }
      sendf(target,"Total of %d players visible to intermud.\n",x);
    }
}	

/*******************************************************
 *
 * _imFingerReq
 *
 *  This function works somewhat like showplayer, but in response
 *  to a finger-req packet from another mud.  It will prepare and
 *  ssend out a finger-reply packet.
 *
 */

DECLARE_HANDLER(_imFingerReq ) 
{
  PERSONA   d;
  int       b;
  int       max_str;
  int    /*   i, w, armor,*/ is_mobile;
  char      *title, *name,ontime[256],level[32],titlestr[512],*p,*q;
  Boolean   in_file;

  if(packet->type[6] != TYPE_STRING)
    {
      sprintf(obuf,"{(\"error\",5,\"%s\",0,\"%s\",\"%s\",\"unk-user\","
                    "\"No target user name given!\"",
                    mudname,
                    packet->data[2],
                    packet->data[3]);
      buildImPacket(packet,obuf);
      __iSend(obuf);
      return;
    }

  if ( (b = fpbns(packet->data[6])) != -1 
       && pvis(b) == 0 
       && b < max_players
       && ptstflg(b, PFL_INTERMUD ) 
       && !ststflg(b, SFL_I_INVIS ) ) {
    in_file = False;
    is_mobile = b >= max_players;
    player2pers(&d,NULL,b);
    name = pname(b);
    title = is_mobile ? NULL : ptitle(b);
    d.p_home = is_mobile ? ploc_reset(b) : phome(b);
    max_str  = is_mobile ? pstr_reset(b) : maxstrength(b);
  } else if (getuaf(packet->data[6], &d)) {
    in_file = True;
    is_mobile = False;
    title = d.p_title;
    name  = d.p_name;
    max_str = pmaxstrength(d.p_level);
  } else {
    sprintf(obuf,"{(\"error\",5,\"%s\",0,\"%s\",\"%s\",\"unk-user\","
                "\"User %s@%s unknown.\",",
            mudname,
            packet->data[2],
            packet->data[3],
            packet->data[6],packet->data[4]);
    buildImPacket(packet,obuf);
    __iSend(obuf);
    return;
  }
 
  /*
   * Ok, let's build up the packet 
   */
  if(is_mobile)
    {
      sprintf(ontime,"Creation");
    }
  else if(!in_file)
    {
      sprintf(ontime,"%s",sec_to_hhmmss (time(0) - plogged_on(b)));
    }
  else
    {
      sprintf(ontime,"%s",time2ascii(d.p_last_on));
    }
  if (plev(b) >= LVL_GOD) sprintf(level,"God");
    else if (plev(b) >= LVL_DEMI) sprintf(level,"Demi-God");
    else if (plev(b) >= LVL_ARCHWIZARD) sprintf(level,"Arch Wizard");
    else if (plev(b) >= LVL_WIZARD) sprintf(level,"Wizard");
    else if (plev(b) > 0 )
     {
       if(in_file)
         sprintf(level,"%s",( (tst_doublebit(&d.p_sflags,SFL_FEMALE))?FLevels:MLevels) [plev(b)]);
       else sprintf(level,"%s",( (ststflg(b,SFL_FEMALE))?FLevels:MLevels)[plev(b)]);
     }
    else sprintf(level,"Mobile");

  /* Escape quotes in the title */
  for( p = make_title(title,name), q = titlestr; *p ; )
    {
      if(*p == '"' || *p == '\\' ) *q++ = '\\';
      *q++ = *p++; 
    }
  *q = '\0';
  sprintf(obuf,"({\"finger-reply\",5,\"%s\",0,\"%s\",\"%s\"," /* packet header */
               "\"%s\"," 	/* User name */
               "\"%s\"," 	/* User title */
 	       "0," 	/* Real Name (nothing for now) */
 	       "0," 	/* Email address */
               "\"%s\","	/* laston/logged in */
               "%ld,"	/* idle time */
               "\"%s\","	/* from where */
               "\"%s\","	/* what level */
               "0,})",
          mudname,packet->data[2],packet->data[3],
          name,
          titlestr,
          ontime,
          (in_file) ? -1 : (time(0) - players[b].last_command),
          "Nowhere for now",
          level);
  __iSend(obuf);
  return;
}

/*******************************************************
 *
 * _imFingerReply
 *
 *  This function handles a finger-reply packet when it comes
 *  in, breaking out the data, and giving the response back to 
 *  the originating user (providing they're still logged in)
 *
 */

DECLARE_HANDLER(_imFingerReply ) 
{
  if(packet->type[5] == TYPE_STRING)
    {
      int target;
      char *misc,*name,*title,*realName,*email,*loggedOn,*where, *level;
      int idle;
      char **types = packet->type;
      char **data = packet->data;

      if( (target = fpbns(data[5])) == -1)
	{
	  /* Bad player, do something here! */
          mudlog("INTERMUD:ARGH - Bad thing found!\n");
	  return;
	}
      if(types[6] == TYPE_STRING) name = data[6]; else name = "Unknown";
      if(types[7] == TYPE_STRING) title = data[7]; else title = "";
      if(types[8] == TYPE_STRING) realName = data[8]; else realName = "";
      if(types[9] == TYPE_STRING) email = data[9]; else email = "Confidential";
      if(types[10] == TYPE_STRING) loggedOn = data[10]; else loggedOn = "Unknown";
      if(types[11] == TYPE_INT) idle = (int)data[11]; else idle = 0;
      if(types[12] == TYPE_STRING) where = data[12]; else where = "Unknown";
      if(types[13] == TYPE_STRING) level = data[13]; else level = "";
      if(types[15] == TYPE_STRING) misc = data[14]; else misc = "";
      
      sendf(target,
            "Finger information on %s from %s\n"
            "%s (%s)\n"
            "Email: %s\n"
            "%s: %s\n"
            "Site: %s\n",
            name,data[2],
            title,realName,
            email,
            (idle==-1)?"Last on: ":"On Since: ",
            loggedOn,
            where);
    }
}
/*******************************************************
 *
 * _imLocateReq
 *
 *  This function handles a locate-req packet, and sends back 
 *  a locate-reply packet if the user being looked for is on
 *  the mud and visible to intermud.
 *
 */

DECLARE_HANDLER(_imLocateReq ) 
{
  if(packet->type[6] == TYPE_STRING)
    {
      int target;

      if( (target = fpbns(packet->data[6])) == -1)
	{
          return;
        }
      if( pvis( target ) > 0 )
        {
          /* User is invis, fail silently */
          return;
        }
      if( !ptstflg( target, PFL_INTERMUD ) )
        {
	  /* User can't access intermud, fail silently */
	  return;
        }
   
      if( ststflg( target, SFL_I_INVIS ) )
        {
 	  /* Target is ignoring intermud, fail silently */
	  return;
	}
      sprintf(obuf,"({\"locate-reply\",5,\"%s\",0,\"%s\",\"%s\","
                   "\"%s\",\"%s\",%ld,0,})",
              mudname,
              packet->data[2],
              packet->data[3],
              mudname,
              pname(target),
              (time(0) - players[target].last_command));
      __iSend(obuf);
    }
}

/*******************************************************
 *
 * _imLocateReply
 *
 *  This function handles a locate-reply packet, informing
 *  the originator of the locate request of who was found/where.
 */

DECLARE_HANDLER(_imLocateReply ) 
{
  if(packet->type[5] == TYPE_STRING)
    {
      int target;

      if( (target = fpbns(packet->data[5])) == -1)
	{
          return;
        }
      sendf(target,"User %s has been found on mud %s\n",
            packet->data[7], packet->data[6]);
    }
}

DECLARE_HANDLER(_imChanlReply ) {}
/*******************************************************
 *
 * _imChanM
 *
 *  Handles a channel-m packet - currently only for the
 *  imud_gossip line.
 *
 */

DECLARE_HANDLER(_imChanM )
{
  if(packet->type[6] == TYPE_STRING)
    {
      /* We have a channelName */
      char *channelName = packet->data[6];

      if(EQ(channelName,"imud_gossip"))
	{
	  char *name = packet->data[7];
	  char *mud = packet->data[2];
	  char *message = packet->data[8];

	  if(!name) name = "Noone";
	  if(!mud) mud = "Nowhere";
	  if(!message) mud = "";
	  send_msg(DEST_ALL,MODE_COLOR|MODE_NSFLAG|MS(SFL_I_NOGOSSIP),
                   LVL_WIZARD,LVL_MAX,NOBODY,NOBODY,
		   "&+m[&+M%s&+m@&+M%s&+m]&* %s\n",
		   name,mud,message);
	}
    }
}

/*******************************************************
 *
 * _imChanE
 * 
 *  Handles a channel-e packet, currently only for the
 *  imud_gossip line.
 * 
 */

DECLARE_HANDLER(_imChanE )
{
  if(packet->type[6] == TYPE_STRING)
    {
      /* We have a channelName */
      char *channelName = packet->data[6];

      if(EQ(channelName,"imud_gossip"))
	{
	  char *name = packet->data[7];
	  char *mud = packet->data[2];
	  char *message = packet->data[8];
	  char outbuf[2048],*p,*q;
	  
	  if(!name) name = "Noone";
	  if(!mud) mud = "Nowhere";
	  if(!message) message = "";

/*	  for(p = message, q = outbuf; *p; *p++,*q++)*/
	  for(p = message, q = outbuf; *p; p++,q++)
	    {
	      if(*p == '$')
		if(*(p+1) == 'N')
		  {
		    char *r;

		    for(r=name;*r;) *q++ = *r++;
		    *q++ = '@';
		    for(r=mud;*r;) *q++ = *r++;
		    p++;
		    q--;
		    continue;
		  }
	      *q = *p;
	    }
	  *q = '\0';
	  send_msg(DEST_ALL,MODE_COLOR|MODE_NSFLAG|MS(SFL_I_NOGOSSIP),
                   LVL_WIZARD,LVL_MAX,NOBODY,NOBODY,
		   "&+m[&+Mig&+m]&* %s\n",outbuf);
		   
	}
    }
}

DECLARE_HANDLER(_imChanT ) {}
DECLARE_HANDLER(_imChanAdd ) {}
DECLARE_HANDLER(_imChanRem ) {}
DECLARE_HANDLER(_imChanAdmin ) {}
DECLARE_HANDLER(_imChanWhoReq ) {}
DECLARE_HANDLER(_imChanWhoReply) {}
DECLARE_HANDLER(_imChanListen) {}
DECLARE_HANDLER(_imChanUserReq) {}
DECLARE_HANDLER(_imChanUserReply ) {}
DECLARE_HANDLER(_imNewsReadReq ) {}
DECLARE_HANDLER(_imNewsPost ) {}
DECLARE_HANDLER(_imNewsListReq ) {}
DECLARE_HANDLER(_imMail ) {}
DECLARE_HANDLER(_imFile ) {}
/*******************************************************
 *
 * _imError
 *
 *    Handles when an error message comes in from the i3 network.
 *
 */

DECLARE_HANDLER(_imError )
{
  if(packet->type[5] == TYPE_STRING)
    {
      /* This error is going to someone. */
      int target;

      if( (target = fpbns(packet->data[5])) == -1)
	{
          if(packet->type[7] == TYPE_STRING)
	      mudlog("INTERMUD: Error: %s\n",packet->data[7]);
	  return;
	}

      if(packet->type[7] == TYPE_STRING)
	sendf(target,"Error: %s\n",
	      packet->data[7]);
    }
  else if ( packet->type[6] == TYPE_STRING )
    {
      if ( EQ( packet->data[6], "bad-name" ) )
	{
	  extern fd_set sockets_fds;
	  FD_CLR( imfd, &sockets_fds );
	  mudlog("INTERMUD: Bad Mud Name, shutting down Intermud Connection.\n");
	  close(imfd);
	  imfd = -1;
	}
    }
}

DECLARE_HANDLER(_imStartup3 ) {}
DECLARE_HANDLER(_imUnknown ) {}

DECLARE_HANDLER(_imShutdown ) {}