nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/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"



//*****************************************************************************
// 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];
  char            next_command[MAX_BUFFER];
  bool            cmd_read;
  bool            bust_prompt;
  bool            closed;
  int             lookup_status;
  int             control;
  int             uid;

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

  HASHTABLE     * 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;
} 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' };


/*
 * 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++;
  }

  /* 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);
    vsprintf(buf, format, args);
    va_end(args);
    text_to_buffer(dsock, 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);
}


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

    // if theres already a command ready, we return
    if(dsock->next_command[0] != '\0')
      return;

    // if there is nothing pending, then return
    if(dsock->inbuf[0] == '\0')
      return;

    // check how long the next command is
    while(dsock->inbuf[size] != '\0' && 
	  dsock->inbuf[size] != '\n' && dsock->inbuf[size] != '\r')
      size++;

    /* we only deal with real commands */
    if(dsock->inbuf[size] == '\0')
      return;

    // copy the next command into next_command
    for(; i < size; i++) {
      if(dsock->inbuf[i] == (signed char) IAC)
	telopt = 1;
      else if(telopt == 1 && (dsock->inbuf[i] == (signed char) DO || 
			      dsock->inbuf[i] == (signed char) DONT))
	telopt = 2;

      // check for compression format
      else if(telopt == 2) {
	unsigned char compress_opt = dsock->inbuf[i];
	telopt = 0;
	
	// check if we're using a valid compression
	if(compress_opt == TELOPT_COMPRESS || compress_opt == TELOPT_COMPRESS2){
	  // start compressing
	  if(dsock->inbuf[i-1] == (signed char) DO)                  
	    compressStart(dsock, compress_opt);
	  // stop compressing
	  else if(dsock->inbuf[i-1] == (signed char) DONT)
	    compressEnd(dsock, compress_opt, FALSE);
	}
      }
      else if(isprint(dsock->inbuf[i]) && isascii(dsock->inbuf[i])) {
	dsock->next_command[j++] = dsock->inbuf[i];
      }
    }
    dsock->next_command[j] = '\0';

    // skip forward to the next line
    while(dsock->inbuf[size] == '\n' || dsock->inbuf[size] == '\r') {
      dsock->cmd_read = TRUE;
      dsock->bust_prompt = TRUE;   // seems like a good place to check
      size++;
    }

    // use i as a static pointer
    i = size;
    
    // move the context of inbuf down
    while(dsock->inbuf[size] != '\0') {
      dsock->inbuf[size - i] = dsock->inbuf[size];
      size++;
    }
    dsock->inbuf[size - i] = '\0';
  }
}



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

  // 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 TRUE;

  if(dsock->bust_prompt) {
    socketShowPrompt(dsock);
    dsock->bust_prompt = FALSE;
  }

  // process all of our outbound text for stuff like colors, etc
  hookRun("process_outbound_text",  hookBuildInfo("sk", dsock));
  hookRun("finalize_outbound_text", hookBuildInfo("sk", dsock));

  // Send the buffer, and return FALSE if the write fails. Clear our output
  success = text_to_socket(dsock, bufferString(dsock->outbuf));
  bufferClear(dsock->outbuf);
  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->input_handlers)   deleteListWith(sock->input_handlers, free);
  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->input_handlers) deleteListWith(sock_new->input_handlers, free);
  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(MAX_BUFFER);
  sock_new->outbuf         = newBuffer(MAX_OUTPUT);
}


/* 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;
      
  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\n", &desc, acct, name, host);
    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);

    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 socket_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);
    
    /* Is there a new command pending ? */
    if (sock->cmd_read) {
      IH_PAIR *pair = listGet(sock->input_handlers, 0);
      if(pair->python == FALSE) {
	void (* handler)(SOCKET_DATA *, char *) = pair->handler;
	handler(sock, sock->next_command);
      }
      else {
	PyObject *arglist = Py_BuildValue("Os", socketGetPyFormBorrowed(sock),
					  sock->next_command);
	PyObject *retval  = PyEval_CallObject(pair->handler, arglist);

	// check for an error:
	if(retval == NULL) {
	  char *tb = getPythonTraceback();
	  if(tb != NULL) {
	    log_string("Error with a python input handler:\r\n%s\r\n", tb);
	    free(tb);
	  }
	}
	
	// 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(sock->next_command));
      if(listSize(sock->command_hist) > 100)
	free(listRemoveNum(sock->command_hist, 100));
      sock->next_command[0] = '\0';
      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
    
    /* 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);
}


int count_pages(const char *string) {
  int num_newlines = count_letters(string, '\n', strlen(string));
  return ((num_newlines / NUM_LINES_PER_PAGE) + 
	  (num_newlines % NUM_LINES_PER_PAGE != 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;

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

  // copy our page to the buf
  while(*page && newline_count < NUM_LINES_PER_PAGE) {
    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(string);
  dsock->curr_page   = 1;
  show_page(dsock, 1);
  if(dsock->tot_pages == 1)
    delete_page(dsock);
}

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);
  
  // 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) || !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\n",
	      sock->control, accountGetName(sock->account), 
	      charGetName(sock->player), sock->hostname);
      // 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)) {
  IH_PAIR *pair = malloc(sizeof(IH_PAIR));
  pair->handler = handler;
  pair->prompt  = prompt;
  pair->python  = FALSE;
  listPush(socket->input_handlers, pair);
}

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

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

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->python) {
    Py_XDECREF((PyObject *)pair->handler);
    Py_XDECREF((PyObject *)pair->prompt);
  }
  free(pair);
}

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

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

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

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) {
      char *tb = getPythonTraceback();
      if(tb != NULL) {
	log_string("Error with a python prompt:\r\n%s\r\n", tb);
	free(tb);
      }
    }
    
    // garbage collection
    Py_XDECREF(retval);
    Py_XDECREF(arglist);
  }
}

void *socketGetAuxiliaryData  ( SOCKET_DATA *sock, const char *name) {
  return hashGet(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;
}



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