nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// socket.c
//
// This file contains the socket code, used for accepting new connections as 
// well as reading and writing to sockets, and closing down unused sockets.
//
//*****************************************************************************

#include "wrapsock.h"
#include <netdb.h>
#include <sys/ioctl.h>
#include <arpa/inet.h> 
#include <zlib.h>
#include <pthread.h>

#include "mud.h"
#include "character.h"
#include "account.h"
#include "save.h"
#include "utils.h"
#include "socket.h"
#include "auxiliary.h"
#include "hooks.h"
#include "scripts/scripts.h"
#include "scripts/pyplugs.h"
#include "dyn_vars/dyn_vars.h"



//*****************************************************************************
// optional modules
//*****************************************************************************
#ifdef MODULE_ALIAS
#include "alias/alias.h"
#endif

// provides a unique identifier number to every socket that connects to the
// mud. Used mostly for referring to sockets in Python
#define START_SOCK_UID           1
int next_sock_uid = START_SOCK_UID;


//
// Here it is... the big ol' datastructure for sockets. Yum.
struct socket_data {
  CHAR_DATA     * player;
  ACCOUNT_DATA  * account;
  char          * hostname;
  char            inbuf[MAX_INPUT_LEN];
  BUFFER        * next_command;
  BUFFER        * iac_sequence;
  // char            next_command[MAX_BUFFER];
  bool            cmd_read;
  bool            bust_prompt;
  bool            closed;
  int             lookup_status;
  int             control;
  int             uid;
  double          idle;          // how many pulses have we been idle for?

  char          * page_string;   // the string that has been paged to us
  int             curr_page;     // the current page we're on
  int             tot_pages;     // the total number of pages the string has
  int             page_cols;     // the screen width in columns
  int             page_rows;     // the screen height in rows
  
  BUFFER        * text_editor;   // where we do our actual work
  BUFFER        * outbuf;        // our buffer of pending output

  LIST          * input_handlers;// a stack of our input handlers and prompts
  LIST          * input;         // lines of input we have received
  LIST          * command_hist;  // the commands we've executed in the past

  unsigned char   compressing;                 /* MCCP support */
  z_stream      * out_compress;                /* MCCP support */
  unsigned char * out_compress_buf;            /* MCCP support */

  AUX_TABLE     * auxiliary;     // auxiliary data installed by other modules
};


//
// contains an input handler and the socket prompt in one structure, so they
// can be stored together in the socket_data. Allows for the option of Python
// or C input handlers and prompt pairs.
typedef struct input_handler_data {
  void *handler; // (* handler)(SOCKET_DATA *, char *);
  void  *prompt; // (*  prompt)(SOCKET_DATA *);
  bool   python;
  char   *state; // what state does this input handler represent?
} IH_PAIR;


//
// required for looking up a socket's IP in a new thread
typedef struct lookup_data {
  SOCKET_DATA    * dsock;   // the socket we wish to do a hostlookup on
  char           * buf;     // the buffer it should be stored in
} LOOKUP_DATA;



/* global variables */
fd_set        fSet;             /* the socket list for polling       */
fd_set        rFd;

/* mccp support */
const unsigned char compress_will   [] = { IAC, WILL, TELOPT_COMPRESS,  '\0' };
const unsigned char compress_will2  [] = { IAC, WILL, TELOPT_COMPRESS2, '\0' };

// used to delete an input handler pair
void deleteInputHandler(IH_PAIR *pair) {
  if(pair->python) {
    Py_XDECREF((PyObject *)pair->handler);
    Py_XDECREF((PyObject *)pair->prompt);
  }
  if(pair->state) free(pair->state);
  free(pair);
}

/*
 * Init_socket()
 *
 * Used at bootup to get a free
 * socket to run the server from.
 */
int init_socket()
{
  struct sockaddr_in my_addr;
  int sockfd, reuse = 1;

  /* let's grab a socket */
  sockfd = socket(AF_INET, SOCK_STREAM, 0);

  /* setting the correct values */
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = INADDR_ANY;
  my_addr.sin_port = htons(mudport);

  /* this actually fixes any problems with threads */
  if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) == -1)
  {
    perror("Error in setsockopt()");
    exit(1);
  } 

  /* bind the port */
  bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr));

  /* start listening already :) */
  listen(sockfd, 3);

  /* return the socket */
  return sockfd;
}


/* 
 * New_socket()
 *
 * Initializes a new socket, get's the hostname
 * and puts it in the active socket_list.
 */
SOCKET_DATA *new_socket(int sock)
{
  struct sockaddr_in   sock_addr;
  pthread_attr_t       attr;
  pthread_t            thread_lookup;
  LOOKUP_DATA        * lData;
  SOCKET_DATA        * sock_new;
  int                  argp = 1;
  socklen_t            size;

  /* initialize threads */
  pthread_attr_init(&attr);   
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  /* create and clear the socket */
  sock_new = calloc(1, sizeof(SOCKET_DATA));

  /* attach the new connection to the socket list */
  FD_SET(sock, &fSet);

  /* clear out the socket */
  clear_socket(sock_new, sock);
  sock_new->closed = FALSE;

  /* set the socket as non-blocking */
  ioctl(sock, FIONBIO, &argp);

  /* update the socket list and table */
  listPut(socket_list, sock_new);
  propertyTablePut(sock_table, sock_new);

  /* do a host lookup */
  size = sizeof(sock_addr);
  if (getpeername(sock, (struct sockaddr *) &sock_addr, &size) < 0)
  {
    perror("New_socket: getpeername");
    sock_new->hostname = strdup("unknown");
  }
  else
  {
    /* set the IP number as the temporary hostname */
    sock_new->hostname = strdup(inet_ntoa(sock_addr.sin_addr));

    if (!compares(sock_new->hostname, "127.0.0.1"))
    {
      /* allocate some memory for the lookup data */
      if ((lData = malloc(sizeof(*lData))) == NULL)
      {
        bug("New_socket: Cannot allocate memory for lookup data.");
        abort();
      }

      /* Set the lookup_data for use in lookup_address() */
      lData->buf    =  strdup((char *) &sock_addr.sin_addr);
      lData->dsock  =  sock_new;

      /* dispatch the lookup thread */
      pthread_create(&thread_lookup, &attr, &lookup_address, (void*) lData);
    }
    else sock_new->lookup_status++;
  }

  /* Store the default columns and rows into the system */
  sock_new->page_cols = SCREEN_WIDTH;
  sock_new->page_rows = NUM_LINES_PER_PAGE;

  /* negotiate compression */
  // text_to_buffer(sock_new, (char *) compress_will2);
  // text_to_buffer(sock_new, (char *) compress_will);

  /* send the greeting */
  // text_to_buffer(sock_new, bufferString(greeting));

  /* everything went as it was supposed to */
  return sock_new;
}


/*
 * Close_socket()
 *
 * Will close one socket directly, freeing all
 * resources and making the socket availably on
 * the socket free_list.
 */
void close_socket(SOCKET_DATA *dsock, bool reconnect)
{
  if (dsock->lookup_status > TSTATE_DONE) return;
  dsock->lookup_status += 2;

  /* remove the socket from the polling list */
  FD_CLR(dsock->control, &fSet);

  /* remove ourself from the list */
  //
  // NO! We don't want to remove ourself from the list just yet.
  // We will do that the next time we show up in the game_loop.
  // removing now will not close the socket completely. Why, I'm not
  // entirely sure... but it doesn't ;)
  //   - Geoff, Dec26/04
  //
  //  listRemove(socket_list, dsock);
  //

  // we have a character, and it's one that's
  // not in the process of being created
  if (dsock->player && charGetUID(dsock->player) != NOBODY) {
    if (reconnect)
      text_to_socket(dsock, "This connection has been taken over.\r\n");
    else {
      charSetSocket(dsock->player, NULL);
      log_string("Closing link to %s", charGetName(dsock->player));
    }
  }
  else if(dsock->player) {
    charSetSocket(dsock->player, NULL);
    extract_mobile(dsock->player);
  }
  
  if(dsock->account) {
    if(account_exists(accountGetName(dsock->account)))
      unreference_account(dsock->account);
    else
      deleteAccount(dsock->account);
  }

  /* set the closed state */
  dsock->closed = TRUE;
  //  dsock->state = STATE_CLOSED;
}


/* 
 * Read_from_socket()
 *
 * Reads one line from the socket, storing it
 * in a buffer for later use. Will also close
 * the socket if it tries a buffer overflow.
 */
bool read_from_socket(SOCKET_DATA *dsock)
{
  int size;
  extern int errno;

  /* check for buffer overflows, and drop connection in that case */
  size = strlen(dsock->inbuf);
  if (size >= sizeof(dsock->inbuf) - 2)
  {
    text_to_socket(dsock, "\n\r!!!! Input Overflow !!!!\n\r");
    return FALSE;
  }

  /* start reading from the socket */
  for (;;)
  {
    int sInput;
    int wanted = sizeof(dsock->inbuf) - 2 - size;

    sInput = read(dsock->control, dsock->inbuf + size, wanted);

    if (sInput > 0)
    {
      size += sInput;

      if (dsock->inbuf[size-1] == '\n' || dsock->inbuf[size-1] == '\r')
        break;
    }
    else if (sInput == 0)
    {
      log_string("Read_from_socket: EOF");
      return FALSE;
    }
    else if (errno == EAGAIN || sInput == wanted)
      break;
    else
    {
      perror("Read_from_socket");
      return FALSE;
    }
  }
  dsock->inbuf[size] = '\0';
  return TRUE;
}


/*
 * Text_to_socket()
 *
 * Sends text directly to the socket,
 * will compress the data if needed.
 */
bool text_to_socket(SOCKET_DATA *dsock, const char *txt)
{
  int iBlck, iPtr, iWrt = 0, length, control = dsock->control;

  length = strlen(txt);

  /* write compressed */
  if (dsock && dsock->out_compress)
  {
    dsock->out_compress->next_in  = (unsigned char *) txt;
    dsock->out_compress->avail_in = length;

    while (dsock->out_compress->avail_in)
    {
      dsock->out_compress->avail_out = COMPRESS_BUF_SIZE - (dsock->out_compress->next_out - dsock->out_compress_buf);

      if (dsock->out_compress->avail_out)
      {
        int status = deflate(dsock->out_compress, Z_SYNC_FLUSH);

        if (status != Z_OK)
        return FALSE;
      }

      length = dsock->out_compress->next_out - dsock->out_compress_buf;
      if (length > 0)
      {
        for (iPtr = 0; iPtr < length; iPtr += iWrt)
        {
          iBlck = UMIN(length - iPtr, 4096);
          if ((iWrt = write(control, dsock->out_compress_buf + iPtr, iBlck)) < 0)
          {
            perror("Text_to_socket (compressed):");
            return FALSE;
          }
        }
        if (iWrt <= 0) break;
        if (iPtr > 0)
        {
          if (iPtr < length)
            memmove(dsock->out_compress_buf, dsock->out_compress_buf + iPtr, length - iPtr);

          dsock->out_compress->next_out = dsock->out_compress_buf + length - iPtr;
        }
      }
    }
    return TRUE;
  }

  /* write uncompressed */
  for (iPtr = 0; iPtr < length; iPtr += iWrt)
  {
    iBlck = UMIN(length - iPtr, 4096);
    if ((iWrt = write(control, txt + iPtr, iBlck)) < 0)
    {
      perror("Text_to_socket:");
      return FALSE;
    }
  }

  return TRUE;
}


void  send_to_socket( SOCKET_DATA *dsock, const char *format, ...) {
  if(format && *format) {
    static char buf[MAX_BUFFER];
    va_list args;
    va_start(args, format);
    vsnprintf(buf, MAX_BUFFER, format, args);
    va_end(args);
    text_to_buffer(dsock, buf);
  }
}

void raw_to_socket( SOCKET_DATA *dsock, const char *format, ...) {
  if(format && *format) {
    static char buf[MAX_BUFFER];
    va_list args;
    va_start(args, format);
    vsnprintf(buf, MAX_BUFFER, format, args);
    va_end(args);

    // Add directly to the buffer, instead of calling text_to_buffer
    bufferCat(dsock->outbuf, buf);
  }
}

/*
 * Text_to_buffer()
 *
 * Stores outbound text in a buffer, where it will
 * stay untill it is flushed in the gameloop.
 *
 * Will also parse ANSI colors and other tags.
 */
void text_to_buffer(SOCKET_DATA *dsock, const char *txt)
{
  // if we're at the head of the outbuf and haven't entered a command, 
  // also copy a newline so we're not printing in front of the prompt
  if(bufferLength(dsock->outbuf) == 0 && !dsock->bust_prompt)
    bufferCat(dsock->outbuf, "\r\n");

  /* add data to buffer */
  bufferCat(dsock->outbuf, txt);
}

//
// read an IAC sequence from the socket's input buffer. When we hit a
// termination point, send a hook and clear he buffer. Return how many
// characters to skip ahead in the input handler
int read_iac_sequence(SOCKET_DATA *dsock, int start) {
  // are we doing subnegotiation?
  bool subneg = strchr(bufferString(dsock->iac_sequence), SB) != NULL;
  int       i = start;
  bool   done = FALSE;
  bool do_iac = FALSE;

  // If we're not in subnegotiation, break the loop at '\0', else wait for code
  // in the loop to break it. '\0' is a potential character in subnegotiation.
  for(; (dsock->inbuf[i] != '\0' && !subneg) || subneg; i++) {
    // are we looking for IAC?
    if(bufferLength(dsock->iac_sequence) == 0) {
      // Oops! Something is broken
      if(dsock->inbuf[i] != (signed char) IAC)
        return 0;
      else
        bufferCatCh(dsock->iac_sequence, IAC);
    }

    // are we looking for a command?
    else if(bufferLength(dsock->iac_sequence) == 1) {
      // did we find subnegotiation?
      if(dsock->inbuf[i] == (signed char) SB) {
        bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
        subneg = TRUE;
      }

      // basic three-character command
      else if(dsock->inbuf[i] == (signed char) WILL || 
              dsock->inbuf[i] == (signed char) WONT || 
              dsock->inbuf[i] == (signed char) DO   || 
              dsock->inbuf[i] == (signed char) DONT)
        bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);

      // something went wrong
      else {
        bufferClear(dsock->iac_sequence);
        return 2;
      }
    }
    
    // are we doing subnegotiation?
    else if(subneg) {
      int       last_i = bufferLength(dsock->iac_sequence) - 1;
      signed char last = bufferString(dsock->iac_sequence)[last_i];
      
      // If we detect an escape sequence of IAC IAC, then only add one IAC to the buffer.
      // Treat SE differently since we have to take extra care of any IAC SE sequence.
      if(dsock->inbuf[i] != (signed char) SE) {
        if(dsock->inbuf[i] == (signed char) IAC) {
        if(!(last == (signed char) IAC && do_iac))
          bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
        do_iac = !do_iac;
      } else {
        // Replace '\032' (decimal 26) with '\031' (decimal 25) for now. '\032' is the
        // hook string delimiter character and, if it is present in this string, it'll
        // break things. Instant MUD crash. Since the only place I've run into '\032' is
        // NAWS negotiation thus far, setting it to one less is acceptable.
        if(dsock->inbuf[i] == '\032')
          bufferCatCh(dsock->iac_sequence, '\031');
        else
          bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
        }
      } else {
        // have hit the end of the subnegotiation? Break out if so
        if(last == (signed char) IAC) {
          if (do_iac)
            bufferCatCh(dsock->iac_sequence, IAC);
          bufferCatCh(dsock->iac_sequence, SE);
          done = TRUE;
          break;
        } else {
          bufferCatCh(dsock->iac_sequence, SE);
        }
      }
    }

    // this is the end
    else {
      bufferCatCh(dsock->iac_sequence, dsock->inbuf[i]);
      done = TRUE;
      break;
    }
  }
  
  int len = i - start + (dsock->inbuf[i] == '\0' ? 0 : 1);

  // broadcast the message we parsed, and prepare for the next sequence
  if(done == TRUE) {
    hookRun("receive_iac", 
        hookBuildInfo("sk str", dsock,bufferString(dsock->iac_sequence)));
    bufferClear(dsock->iac_sequence);
  }
  return len;
}

void next_cmd_from_buffer(SOCKET_DATA *dsock) {
  // do we have stuff in our input list? If so, use that instead of inbuf
  dsock->cmd_read = FALSE;
  if(listSize(dsock->input) > 0) {
    char *cmd = listPop(dsock->input);
    bufferClear(dsock->next_command);
    bufferCat(dsock->next_command, cmd);
    dsock->cmd_read    = TRUE;
    dsock->bust_prompt = TRUE;
    free(cmd);
  }
  else {
    int i = 0, cmd_end = -1;

    // are we building an IAC command? Try to continue it
    if(bufferLength(dsock->iac_sequence) > 0)
      i += read_iac_sequence(dsock, 0);

    // copy over characters until we hit a newline, an IAC command, or \0
    for(; dsock->inbuf[i] != '\0' && cmd_end < 0; i++) {
      switch(dsock->inbuf[i]) {
      default:
    // append us to the command
    bufferCatCh(dsock->next_command, dsock->inbuf[i]);
    break;
      case '\n':
    // command end found
    cmd_end = ++i;
      case '\r':
    // ignore \r ... only pay attention to \n
    break;
      case (signed char) IAC:
    i += read_iac_sequence(dsock, i) - 1;
    break;
      }
      if(cmd_end >= 0)
    break;
    }

    // move the context of inbuf down
    int begin = 0;
    while(dsock->inbuf[i] != '\0')
      dsock->inbuf[begin++] = dsock->inbuf[i++];
    dsock->inbuf[begin] = '\0';
    
    // did we find a command?
    if(cmd_end >= 0) {
      dsock->cmd_read    = TRUE;
      dsock->bust_prompt = TRUE;
    }
  }
}



bool flush_output(SOCKET_DATA *dsock) {
  bool  success = TRUE;
  BUFFER   *buf = NULL;

  // run any hooks prior to flushing our text
  hookRun("flush", hookBuildInfo("sk", dsock));

  // quit if we have no output and don't need/can't have a prompt
  if(bufferLength(dsock->outbuf) <= 0 && 
     (!dsock->bust_prompt || !socketHasPrompt(dsock)))
    return success;

  buf = newBuffer(1);

  // send our outbound text
  if(bufferLength(dsock->outbuf) > 0) {
    hookRun("process_outbound_text",  hookBuildInfo("sk", dsock));
    hookRun("finalize_outbound_text", hookBuildInfo("sk", dsock));
    //success = text_to_socket(dsock, bufferString(dsock->outbuf));
    bufferCat(buf, bufferString(dsock->outbuf));
    bufferClear(dsock->outbuf);
  }

  // send our prompt
  if(dsock->bust_prompt && success) {
    socketShowPrompt(dsock);
    hookRun("process_outbound_prompt",  hookBuildInfo("sk", dsock));
    hookRun("finalize_outbound_prompt", hookBuildInfo("sk", dsock));
    //success = text_to_socket(dsock, bufferString(dsock->outbuf));
    bufferCat(buf, bufferString(dsock->outbuf));
    bufferClear(dsock->outbuf);
    dsock->bust_prompt = FALSE;
  }

  success = text_to_socket(dsock, bufferString(buf));
  deleteBuffer(buf);

  // return our success
  return success;
}



//*****************************************************************************
//
// SOCKET MAINTENANCE
//
// Functions below this point are mainly concerned with the upkeep and
// maintenance of sockets (making sure they are initialized, garbage collected, 
// getting their addresses, etc...)
//
//*****************************************************************************
void deleteSocket(SOCKET_DATA *sock) {
  if(sock->hostname)      free(sock->hostname);
  if(sock->page_string)   free(sock->page_string);
  if(sock->text_editor)   deleteBuffer(sock->text_editor);
  if(sock->outbuf)        deleteBuffer(sock->outbuf);
  if(sock->next_command)  deleteBuffer(sock->next_command);
  if(sock->iac_sequence)  deleteBuffer(sock->iac_sequence);
  if(sock->input_handlers)deleteListWith(sock->input_handlers,deleteInputHandler);
  if(sock->input)         deleteListWith(sock->input, free);
  if(sock->command_hist)  deleteListWith(sock->command_hist, free);
  if(sock->auxiliary)     deleteAuxiliaryData(sock->auxiliary);
  free(sock);
}

void clear_socket(SOCKET_DATA *sock_new, int sock)
{
  if(sock_new->page_string)    free(sock_new->page_string);
  if(sock_new->text_editor)    deleteBuffer(sock_new->text_editor);
  if(sock_new->outbuf)         deleteBuffer(sock_new->outbuf);
  if(sock_new->next_command)   deleteBuffer(sock_new->next_command);
  if(sock_new->iac_sequence)   deleteBuffer(sock_new->iac_sequence);
  if(sock_new->input_handlers) deleteListWith(sock_new->input_handlers, deleteInputHandler);
  if(sock_new->auxiliary)      deleteAuxiliaryData(sock_new->auxiliary);
  if(sock_new->input)          deleteListWith(sock_new->input, free);
  if(sock_new->command_hist)   deleteListWith(sock_new->command_hist, free);

  bzero(sock_new, sizeof(*sock_new));
  sock_new->auxiliary = newAuxiliaryData(AUXILIARY_TYPE_SOCKET);
  sock_new->input_handlers = newList();
  sock_new->input          = newList();
  sock_new->command_hist   = newList();
  sock_new->control        = sock;
  sock_new->lookup_status  = TSTATE_LOOKUP;
  sock_new->uid            = next_sock_uid++;

  sock_new->text_editor    = newBuffer(1);
  sock_new->outbuf         = newBuffer(MAX_OUTPUT);
  sock_new->next_command   = newBuffer(1);
  sock_new->iac_sequence   = newBuffer(1);
}


/* does the lookup, changes the hostname, and dies */
void *lookup_address(void *arg)
{
  LOOKUP_DATA *lData = (LOOKUP_DATA *) arg;
  struct hostent *from = 0;
  //  struct hostent ent;
  //  char buf[16384];
  //  int err;

  /* do the lookup and store the result at &from */
  from = gethostbyaddr(lData->buf, sizeof(lData->buf), AF_INET);

  /* did we get anything ? */
  if (from && from->h_name)
  {
    free(lData->dsock->hostname);
    lData->dsock->hostname = strdup(from->h_name);
  }

  /* set it ready to be closed or used */
  lData->dsock->lookup_status++;

  /* free the lookup data */
  free(lData->buf);
  free(lData);

  /* and kill the thread */
  pthread_exit(0);
  return NULL;
}


void recycle_sockets()
{
  SOCKET_DATA *dsock;
  LIST_ITERATOR *sock_i = newListIterator(socket_list);

  ITERATE_LIST(dsock, sock_i) {
    if (dsock->lookup_status != TSTATE_CLOSED) 
      continue;

    /* remove the socket from the main list */
    listRemove(socket_list, dsock);
    propertyTableRemove(sock_table, dsock->uid);

    /* close the socket */
    close(dsock->control);

    /* stop compression */
    compressEnd(dsock, dsock->compressing, TRUE);

    /* delete the socket from memory */
    deleteSocket(dsock);
  } deleteListIterator(sock_i);
}


/* reset all of the sockets' control values */
void reconnect_copyover_sockets() {
  LIST_ITERATOR *sock_i = newListIterator(socket_list);
  SOCKET_DATA     *sock = NULL; 
  ITERATE_LIST(sock, sock_i)
    FD_SET(sock->control, &fSet);
  deleteListIterator(sock_i);
}


/* Recover from a copyover - load players */
void copyover_recover() {     
  CHAR_DATA    *dMob;
  ACCOUNT_DATA *account;
  SOCKET_DATA  *dsock;
  FILE *fp;
  char acct[100];
  char name[100];
  char host[MAX_BUFFER];
  int desc;
  int cols;
  int rows;
      
  log_string("Copyover recovery initiated");

  if ((fp = fopen(COPYOVER_FILE, "r")) == NULL) {  
    log_string("Copyover file not found. Exitting.");
    exit (1);
  }
      
  /* In case something crashes - doesn't prevent reading */
  unlink(COPYOVER_FILE);

  for (;;) {  
    fscanf(fp, "%d %s %s %s %d %d\n", &desc, acct, name, host, &cols, &rows);
    if (desc == -1)
      break;

    // Many thanks to Rhaelar for the help in finding this bug; clear_socket
    // does not like receiving freshly malloc'd data. We have to make sure
    // everything is zeroed before we pass it to clear_socket
    //    dsock = malloc(sizeof(*dsock));
    dsock = calloc(1, sizeof(*dsock));
    clear_socket(dsock, desc);

    // Restore the screen size variables.
    dsock->page_cols = cols;
    dsock->page_rows = rows;

    dsock->hostname = strdup(host);
    listPut(socket_list, dsock);
    propertyTablePut(sock_table, dsock);

    // load account data
    if((account = get_account(acct)) != NULL)
      socketSetAccount(dsock, account);
    // no luck!
    else {
      close_socket(dsock, FALSE);
      continue;
    }

    // load player data
    if ((dMob = get_player(name)) != NULL) {
      // attach to socket
      charSetSocket(dMob, dsock);
      socketSetChar(dsock, dMob);

      // try putting the character into the game
      // close the socket if we fail.
      if(!try_enter_game(dMob)) {
    // do not bother extracting, since we haven't entered the game yet
    unreference_player(socketGetChar(dsock));
    socketSetChar(dsock, NULL);
    close_socket(dsock, FALSE);
    continue;
      }
    }
    // no luck
    else {
      close_socket(dsock, FALSE);
      continue;
    }
   
    // Write something, and check if it goes error-free
    if (!text_to_socket(dsock, "\n\r <*>  And before you know it, everything has changed  <*>\n\r")) { 
      close_socket(dsock, FALSE);
      continue;
    }
  
    // make sure the socket can be used
    dsock->bust_prompt    =  TRUE;
    dsock->lookup_status  =  TSTATE_DONE;

    // let our modules know we've finished copying over a socket
    hookRun("copyover_complete", hookBuildInfo("sk", dsock));

    // negotiate compression
    text_to_buffer(dsock, (char *) compress_will2);
    text_to_buffer(dsock, (char *) compress_will);
  }
  fclose(fp);

  // now, set all of the sockets' control to the new fSet
  reconnect_copyover_sockets();
}     

void output_handler() {
  LIST_ITERATOR *sock_i = newListIterator(socket_list);
  SOCKET_DATA     *sock = NULL; 

  ITERATE_LIST(sock, sock_i) {
    /* if the player quits or get's disconnected */
    if(sock->closed)
      continue;
    
    /* Send all new data to the socket and close it if any errors occour */
    if (!flush_output(sock))
      close_socket(sock, FALSE);
  } deleteListIterator(sock_i);
}

void input_handler() {
  LIST_ITERATOR *sock_i = newListIterator(socket_list);
  SOCKET_DATA     *sock = NULL; 

  ITERATE_LIST(sock, sock_i) {
    // Close sockects we are unable to read from, or if we have no handler
    // to take in input
    if ((FD_ISSET(sock->control, &rFd) && !read_from_socket(sock)) ||
    listSize(sock->input_handlers) == 0) {
      close_socket(sock, FALSE);
      continue;
    }

    /* Ok, check for a new command */
    next_cmd_from_buffer(sock);
    
    // are we idling?
    if(!sock->cmd_read)
      sock->idle +=  1.0 / PULSES_PER_SECOND;
    /* Is there a new command pending ? */
    else if (sock->cmd_read) {
      sock->idle = 0.0;
      IH_PAIR *pair = listGet(sock->input_handlers, 0);
      if(pair->python == FALSE) {
    void (* handler)(SOCKET_DATA *, char *) = pair->handler;
    char *cmddup = strdup(bufferString(sock->next_command));
    handler(sock, cmddup);
    free(cmddup);
      }
      else {
    PyObject *arglist = Py_BuildValue("Os", socketGetPyFormBorrowed(sock),
                      bufferString(sock->next_command));
    PyObject *retval  = PyEval_CallObject(pair->handler, arglist);

    // check for an error:
    if(retval == NULL)
      log_pyerr("Error with a Python input handler");
    
    // garbage collection
    Py_XDECREF(retval);
    Py_XDECREF(arglist);
      }

      // append our last command to the command history. History buffer is
      // 100 commands, so pop off the earliest command if we're going over
      listPut(sock->command_hist, strdup(bufferString(sock->next_command)));
      if(listSize(sock->command_hist) > 100)
    free(listRemoveNum(sock->command_hist, 100));
      bufferClear(sock->next_command);

      // we save whether or not we read a command until our next call to
      // input_handler(), at which time it is reset to FALSE if we didn't read
      // sock->cmd_read = FALSE;
    }

#ifdef MODULE_ALIAS
    // ACK!! this is so yucky, but I can't think of a better way to do it...
    // if this command was put in place by an alias, decrement the alias_queue
    // counter by one. This counter is in place mainly so aliases do not end
    // up calling eachother and making us get stuck in an infinite loop.
    if(sock->player) {
      int alias_queue = charGetAliasesQueued(sock->player);
      if(alias_queue > 0)
    charSetAliasesQueued(sock->player, --alias_queue);
    }
#endif
  } deleteListIterator(sock_i);
}


int count_pages(SOCKET_DATA *dsock, const char *string) {
  int num_newlines = count_letters(string, '\n', strlen(string));
  int rows = dsock->page_rows - 3;
  if(rows <= 0)
    rows = 1;
  
  // if we just have one extra line, ignore the paging prompt and just send
  // the entire thing
  if(num_newlines <= rows + 1)
    return 1;

  return ((num_newlines / rows) + 
      (num_newlines % rows != 0));
}

void show_page(SOCKET_DATA *dsock, int page_num) {
  static char buf[MAX_BUFFER];
  char *page = dsock->page_string;
  *buf = '\0';
  int buf_i = 0, newline_count = 0, curr_page = 1;
  int rows = dsock->page_rows - 3;
  if(rows <= 0)
    rows = 1;

  // skip to our page
  while(*page && curr_page < page_num) {
    if(*page == '\n')
      newline_count++;
    if(newline_count >= rows) {
      curr_page++;
      newline_count = 0;
    }
    page++;
  }

  // copy our page to the buf
  while(*page && newline_count < rows) {
    if(*page == '\n')
      newline_count++;
    buf[buf_i] = *page;
    page++;
    buf_i++;
  }

  buf[buf_i] = '\0';
  text_to_buffer(dsock, buf);
  if(dsock->tot_pages != 1)
    send_to_socket(dsock, "{c[{ypage %d of %d. Use MORE to view next page or BACK to view previous{c]{n\r\n",
           page_num, dsock->tot_pages);
}


void delete_page(SOCKET_DATA *dsock) {
  if(dsock->page_string) free(dsock->page_string);
  dsock->page_string = NULL;
  dsock->tot_pages = 0;
  dsock->curr_page = 0;
}

void  page_string(SOCKET_DATA *dsock, const char *string) {
  if(dsock->page_string) 
    free(dsock->page_string);
  dsock->page_string = strdup(string);
  dsock->tot_pages   = count_pages(dsock, string);
  
  if(dsock->tot_pages == 1) {
    text_to_buffer(dsock, dsock->page_string);
    delete_page(dsock);
  }
  else {
    dsock->curr_page = 1;
    show_page(dsock, 1);
  }
}

void page_back(SOCKET_DATA *dsock) {
  dsock->curr_page--;
  if(dsock->tot_pages > 0 && dsock->curr_page <= dsock->tot_pages &&
     dsock->curr_page > 0)
    show_page(dsock, dsock->curr_page);
  else {
    dsock->curr_page = MIN(dsock->tot_pages, 1);
    text_to_buffer(dsock, "There is no more text in your page buffer.\r\n");
  }
  /*
  if(dsock->curr_page >= dsock->tot_pages)
    delete_page(dsock);
  */
}

void  page_continue(SOCKET_DATA *dsock) {
  dsock->curr_page++;
  if(dsock->tot_pages > 0 && dsock->curr_page > 0 && 
     dsock->curr_page <= dsock->tot_pages)
    show_page(dsock, dsock->curr_page);
  else {
    dsock->curr_page = dsock->tot_pages;
    text_to_buffer(dsock, "There is no more text in your page buffer.\r\n");
  }
}


//
// the command handler for the reader
void read_handler(SOCKET_DATA *sock, char *input) {
  if(!strncasecmp(input, "more", strlen(input)))
    page_continue(sock);
  else if(!strncasecmp(input, "back", strlen(input)))
    page_back(sock);
  else if(!strncasecmp(input, "quit", strlen(input)))
    socketPopInputHandler(sock);
  else
    text_to_buffer(sock, "Invalid choice!\r\n");
}


//
// the prompt for reading text
void read_prompt(SOCKET_DATA *sock) {
  text_to_buffer(sock, "\r\nQ to stop reading> ");
}

//
// a new handler that allows people to read long bits of text
void  start_reader(SOCKET_DATA *dsock, const char *text) {
  // add a new input handler to control the reading
  socketPushInputHandler(dsock, read_handler, read_prompt, "read text");
  
  // page the string
  page_string(dsock, text);
}


void do_copyover(void) {
  LIST_ITERATOR *sock_i = newListIterator(socket_list);
  SOCKET_DATA     *sock = NULL;
  FILE *fp;
  char buf[100];
  char control_buf[20];
  char port_buf[20];

  if ((fp = fopen(COPYOVER_FILE, "w+")) == NULL)
    return;

  sprintf(buf, "\n\r <*>            The world starts spinning             <*>\n\r");

  // For each playing descriptor, save its character and account
  ITERATE_LIST(sock, sock_i) {
    compressEnd(sock, sock->compressing, FALSE);
    // kick off anyone who hasn't yet logged in a character
    if (!socketGetChar(sock) || !socketGetAccount(sock) || 
    !charGetRoom(socketGetChar(sock))) {
      text_to_socket(sock, "\r\nSorry, we are rebooting. Come back in a few minutes.\r\n");
      close_socket(sock, FALSE);
    }
    // save account and player info to file
    else {
      fprintf(fp, "%d %s %s %s %d %d\n",
          sock->control, accountGetName(sock->account), 
          charGetName(sock->player), sock->hostname,
          sock->page_cols, sock->page_rows);
      // save the player
      save_player(sock->player);
      save_account(sock->account);
      text_to_socket(sock, buf);
    }
  } deleteListIterator(sock_i);
  
  fprintf (fp, "-1\n");
  fclose (fp);

  // close any pending sockets
  recycle_sockets();

#ifdef MODULE_WEBSERVER
  // if we have a webserver set up, finalize that
  finalize_webserver();
#endif
  
  // exec - descriptors are inherited
  sprintf(control_buf, "%d", control);
  sprintf(port_buf, "%d", mudport);
  execl(EXE_FILE, "NakedMud", "-copyover", control_buf, port_buf, NULL);
}



//*****************************************************************************
// get and set functions
//*****************************************************************************
CHAR_DATA *socketGetChar     ( SOCKET_DATA *dsock) {
  return dsock->player;
}

void       socketSetChar     ( SOCKET_DATA *dsock, CHAR_DATA *ch) {
  dsock->player = ch;
}

ACCOUNT_DATA *socketGetAccount ( SOCKET_DATA *dsock) {
  return dsock->account;
}

void socketSetAccount (SOCKET_DATA *dsock, ACCOUNT_DATA *account) {
  dsock->account = account;
}

BUFFER *socketGetTextEditor   ( SOCKET_DATA *sock) {
  return sock->text_editor;
}

BUFFER *socketGetOutbound    ( SOCKET_DATA *sock) {
  return sock->outbuf;
}

void socketPushInputHandler  ( SOCKET_DATA *socket, 
                   void handler(SOCKET_DATA *socket, char *input),
                   void prompt (SOCKET_DATA *socket),
                   const char *state) {
  IH_PAIR *pair = malloc(sizeof(IH_PAIR));
  pair->handler = handler;
  pair->prompt  = prompt;
  pair->python  = FALSE;
  pair->state   = strdupsafe(state);
  listPush(socket->input_handlers, pair);
}

void socketPushPyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt,
                  const char *state) {
  IH_PAIR *pair = malloc(sizeof(IH_PAIR));
  pair->handler = handler;
  pair->prompt  = prompt;
  pair->python  = TRUE;
  pair->state   = strdupsafe(state);
  Py_XINCREF((PyObject *)handler);
  Py_XINCREF((PyObject *)prompt);
  listPush(sock->input_handlers, pair);
}

void socketReplacePyInputHandler(SOCKET_DATA *sock, void *handler,void *prompt,
                 const char *state){
  socketPopInputHandler(sock);
  socketPushPyInputHandler(sock, handler, prompt, state);
}

const char *socketGetLastCmd(SOCKET_DATA *sock) {
  if(listSize(sock->command_hist) == 0)
    return "";
  else
    return listHead(sock->command_hist);
}

void socketPopInputHandler   ( SOCKET_DATA *socket) {
  IH_PAIR *pair = listPop(socket->input_handlers);
  if(pair != NULL)
    deleteInputHandler(pair);
}

void socketReplaceInputHandler( SOCKET_DATA *socket,
                void handler(SOCKET_DATA *socket, char *input),
                void prompt (SOCKET_DATA *socket),
                const char *state) {
  socketPopInputHandler(socket);
  socketPushInputHandler(socket, handler, prompt, state);
}

void socketQueueCommand( SOCKET_DATA *sock, const char *cmd) {
  listQueue(sock->input, strdup(cmd));
}

bool socketHasCommand(SOCKET_DATA *sock) {
  return sock->cmd_read;
}

int socketGetUID( SOCKET_DATA *dsock) {
  return dsock->uid;
}

int socketGetCols( SOCKET_DATA *dsock) {
  return dsock->page_cols;
}

int socketGetRows( SOCKET_DATA *dsock) {
  return dsock->page_rows;
}

void socketSetCols( SOCKET_DATA *dsock, int cols) {
  dsock->page_cols = cols;
}

void socketSetRows( SOCKET_DATA *dsock, int rows) {
  dsock->page_rows = rows;
}

bool socketHasPrompt(SOCKET_DATA *sock) {
  IH_PAIR *pair = listGet(sock->input_handlers, 0);
  return (pair != NULL && pair->prompt != NULL);
}

void socketShowPrompt( SOCKET_DATA *sock) {
  IH_PAIR *pair = listGet(sock->input_handlers, 0);
  if(pair == NULL || pair->prompt == NULL)
    return;
  else if(pair->python == FALSE) {
    ((void (*)(SOCKET_DATA *))pair->prompt)(sock);
  }
  else {
    PyObject *arglist = Py_BuildValue("(O)", socketGetPyFormBorrowed(sock));
    PyObject *retval  = PyEval_CallObject(pair->prompt, arglist);
    // check for an error:
    if(retval == NULL)
      log_pyerr("Error with a Python prompt");
    
    // garbage collection
    Py_XDECREF(retval);
    Py_XDECREF(arglist);
  }
}

void *socketGetAuxiliaryData  ( SOCKET_DATA *sock, const char *name) {
  return auxiliaryGet(sock->auxiliary, name);
}

const char *socketGetHostname(SOCKET_DATA *sock) {
  return sock->hostname;
}

int socketGetDNSLookupStatus(SOCKET_DATA *sock) {
  return sock->lookup_status;
}

void socketBustPrompt(SOCKET_DATA *sock) {
  sock->bust_prompt = TRUE;
}

const char *socketGetState(SOCKET_DATA *sock) {
  IH_PAIR *pair = listGet(sock->input_handlers, 0);
  if(pair == NULL) 
    return "";
  else
    return pair->state;
}

double socketGetIdleTime(SOCKET_DATA *sock) {
  return sock->idle;
}



//*****************************************************************************
// MCCP SUPPORT IS BELOW THIS LINE. NOTHING BUT MCCP SUPPORT SHOULD GO BELOW
// THIS LINE.
//*****************************************************************************
/*
 * mccp.c - support functions for the Mud Client Compression Protocol
 *
 * see http://www.randomly.org/projects/MCCP/
 *
 * Copyright (c) 1999, Oliver Jowett <oliver@randomly.org>
 *
 * This code may be freely distributed and used if this copyright
 * notice is retained intact.
 */
/*
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include "mud.h"
#include "socket.h"
#include "utils.h"
*/

/* local functions */
bool  processCompressed       ( SOCKET_DATA *dsock );

const unsigned char enable_compress  [] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, 0 };
const unsigned char enable_compress2 [] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE, 0 };

/*
 * Memory management - zlib uses these hooks to allocate and free memory
 * it needs
 */
void *zlib_alloc(void *opaque, unsigned int items, unsigned int size)
{
  return calloc(items, size);
}

void zlib_free(void *opaque, void *address)
{
  free(address);
}

/*
 * Begin compressing data on `desc'
 */
bool compressStart(SOCKET_DATA *dsock, unsigned char teleopt)
{
  z_stream *s;

  /* already compressing */
  if (dsock->out_compress)
    return TRUE;

  /* allocate and init stream, buffer */
  s = (z_stream *) malloc(sizeof(*s));
  dsock->out_compress_buf = (unsigned char *) malloc(COMPRESS_BUF_SIZE);

  s->next_in    =  NULL;
  s->avail_in   =  0;
  s->next_out   =  dsock->out_compress_buf;
  s->avail_out  =  COMPRESS_BUF_SIZE;
  s->zalloc     =  zlib_alloc;
  s->zfree      =  zlib_free;
  s->opaque     =  NULL;

  if (deflateInit(s, 9) != Z_OK)
  {
    free(dsock->out_compress_buf);
    free(s);
    return FALSE;
  }

  /* version 1 or 2 support */
  if (teleopt == TELOPT_COMPRESS)
    text_to_socket(dsock, (char *) enable_compress);
  else if (teleopt == TELOPT_COMPRESS2)
    text_to_socket(dsock, (char *) enable_compress2);
  else
  {
    bug("Bad teleoption %d passed", teleopt);
    free(dsock->out_compress_buf);
    free(s);
    return FALSE;
  }

  /* now we're compressing */
  dsock->compressing = teleopt;
  dsock->out_compress = s;

  /* success */
  return TRUE;
}

/* Cleanly shut down compression on `desc' */
bool compressEnd(SOCKET_DATA *dsock, unsigned char teleopt, bool forced)
{
  unsigned char dummy[1];

  if (!dsock->out_compress)
    return TRUE;

  if (dsock->compressing != teleopt)
    return FALSE;

  dsock->out_compress->avail_in = 0;
  dsock->out_compress->next_in = dummy;

  /* No terminating signature is needed - receiver will get Z_STREAM_END */
  if (deflate(dsock->out_compress, Z_FINISH) != Z_STREAM_END && !forced)
    return FALSE;

  /* try to send any residual data */
  if (!processCompressed(dsock) && !forced)
    return FALSE;

  /* reset compression values */
  deflateEnd(dsock->out_compress);
  free(dsock->out_compress_buf);
  free(dsock->out_compress);
  dsock->compressing      = 0;
  dsock->out_compress     = NULL;
  dsock->out_compress_buf = NULL;

  /* success */
  return TRUE;
}

/* Try to send any pending compressed-but-not-sent data in `desc' */
bool processCompressed(SOCKET_DATA *dsock)
{
  int iStart, nBlock, nWrite, len;

  if (!dsock->out_compress)
    return TRUE;
    
  len = dsock->out_compress->next_out - dsock->out_compress_buf;
  if (len > 0)
  {
    for (iStart = 0; iStart < len; iStart += nWrite)
    {
      nBlock = UMIN (len - iStart, 4096);
      if ((nWrite = write(dsock->control, dsock->out_compress_buf + iStart, nBlock)) < 0)
      {
        if (errno == EAGAIN/* || errno == ENOSR*/)
          break;

        /* write error */
        return FALSE;
      }
      if (nWrite <= 0)
        break;
    }

    if (iStart)
    {
      if (iStart < len)
        memmove(dsock->out_compress_buf, dsock->out_compress_buf+iStart, len - iStart);

      dsock->out_compress->next_out = dsock->out_compress_buf + len - iStart;
    }
  }

  /* success */
  return TRUE;
}

//
// compress output
//
COMMAND(cmd_compress)
{
  /* no socket, no compression */
  if (!charGetSocket(ch))
    return;

  /* enable compression */
  if (!charGetSocket(ch)->out_compress) {
    text_to_char(ch, "Trying compression.\n\r");
    text_to_buffer(charGetSocket(ch), (char *) compress_will2);
    text_to_buffer(charGetSocket(ch), (char *) compress_will);
  }
  else /* disable compression */ {
    if (!compressEnd(charGetSocket(ch), charGetSocket(ch)->compressing, FALSE)){
      text_to_char(ch, "Failed.\n\r");
      return;
    }
    text_to_char(ch, "Compression disabled.\n\r");
  }
}
//*****************************************************************************
// IF YOU ARE PUTTING ANYTHING BELOW THIS LINE, YOU ARE PUTTING IT IN THE WRONG
// PLACE!! ALL SOCKET-RELATED STUFF SHOULD GO UP ABOVE THE MCCP SUPPORT STUFF!
//*****************************************************************************