phantasia4/
phantasia4/conf/
phantasia4/phantasia/bin/
phantasia4/phantasia/src/utilities/
phantasia4/public_html/cgi-bin/
/*
 * socket.c - routines to handle all socket matters for Phantasia
 */

#include "include.h"

extern void *Do_start_thread(void *c);
extern server_hook;
extern randData;

/************************************************************************
/
/ FUNCTION NAME: Do_initialize_socket(struct server_t *server)
/
/ FUNCTION: To initialize the program's socket
/
/ AUTHOR: Brian Kelly, 4/12/99
/
/ ARGUMENTS:
/       struct server_t *s - address of the server's main data strcture
/
/ RETURN VALUE: 
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
/           - strip non-printing characters (unless Wizard)
/           - echo, if desired
/           - redraw the screen if CH_REDRAW is entered
/           - read in only 'mx - 1' characters or less characters
/           - nul-terminate string, and throw away newline
/
/       'mx' is assumed to be at least 2.
/
*************************************************************************/

int Do_init_server_socket()
{
    struct sockaddr_in bind_address;
    char error_msg[SZ_ERROR_MESSAGE];
    int the_socket, error, on=1;

	/* create a socket */
    errno = 0;
    if ((the_socket=socket(AF_INET, SOCK_STREAM, 0)) == -1) {

        sprintf(error_msg,
	   "[0.0.0.0:?] Socket creation failed in Do_init_server_socket: %s\n",
	   strerror(errno));

        Do_log_error(error_msg);
        exit(SOCKET_CREATE_ERROR);
    }

    error = setsockopt(the_socket, SOL_SOCKET, SO_REUSEADDR,
            (char *) &on, sizeof(on));

    if (error != 0) {

        sprintf(error_msg, "[0.0.0.0:?] setsockopt failed with error code of %d in Do_init_server_socket.\n", error);

        Do_log_error(error_msg);
        exit(SOCKET_CREATE_ERROR);
    }

	/* set up the bind address */
    bind_address.sin_family = AF_INET;
    bind_address.sin_addr.s_addr = INADDR_ANY;
    bind_address.sin_port = PHANTASIA_PORT;

	/* bind to that socket */
    error = bind(the_socket, (struct sockaddr *) &bind_address,
	    sizeof(bind_address));

    if (error != 0) {

        sprintf(error_msg, "[0.0.0.0:?] bind to socket failed with error code of %d in Do_init_server_socket.\n", error);

        Do_log_error(error_msg);
        exit(SOCKET_BIND_ERROR);
    }

	/* start listening on the socket */
    error = listen(the_socket, SOMAXCONN);
    if (error != 0) {

        sprintf(error_msg, "[0.0.0.0:?] listen command failed with error code of %d in Do_init_server_socket\n", error);

        Do_log_error(error_msg);
        exit(SOCKET_LISTEN_ERROR);
    }

    if (error = fcntl(the_socket, F_SETOWN, getpid()) < 0) {

	sprintf(error_msg, "[0.0.0.0:?] fcntl F_SETOWN failed with error code of %d in Do_init_server_socket.\n", error);

        Do_log_error(error_msg);
	exit(SOCKET_BIND_ERROR);
    }

    if (error = fcntl(the_socket, F_SETFL, O_ASYNC) < 0) {

	sprintf(error_msg, "[0.0.0.0:?] fcntl F_SETFL failed with error code of %d in Do_init_server_socket.\n", error);

        Do_log_error(error_msg);
	exit(SOCKET_BIND_ERROR);
    }

    return the_socket; /* no problems */
}


/************************************************************************
/
/ FUNCTION NAME: Do_accept_connections(struct server_t *s)
/
/ FUNCTION: Create new games for new connections on the socket
/
/ AUTHOR: Brian Kelly, 4/12/99
/
/ ARGUMENTS:
/       struct server_t *s - address of the server's main data strcture
/
/ RETURN VALUE: int error
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
/           - strip non-printing characters (unless Wizard)
/           - echo, if desired
/           - redraw the screen if CH_REDRAW is entered
/           - read in only 'mx - 1' characters or less characters
/           - nul-terminate string, and throw away newline
/
/       'mx' is assumed to be at least 2.
/
*************************************************************************/

int Do_accept_connections(struct server_t *s)
{
    struct game_t *game_ptr;
    struct client_t *client_ptr;
    pthread_attr_t thread_attr;
    size_t addrlen;
    char error_msg[SZ_ERROR_MESSAGE];
    int theError, on=1, terms, itemp;
    char gethost_buffer[16384], *string_ptr, *string_ptr_two;
    struct hostent *host_info, host_buffer;
    struct in_addr theNetwork;

    while (Do_check_socket(s->the_socket)) {

	    /* on a new connection, seed the random number generator */
	srandom_r (time(NULL), (struct random_data *)&randData);

	    /* create a structure for the thread information */
        client_ptr = (struct client_t *) Do_malloc(SZ_CLIENT);

	    /* accept the new connection */
	addrlen = sizeof(client_ptr->address);

	errno = 0;
        client_ptr->socket = accept(s->the_socket,
		(struct sockaddr *)&client_ptr->address, &addrlen);

        if (client_ptr->socket == -1) {

            sprintf(error_msg,
	   "[0.0.0.0:%d] accept socket failed in Do_accept_connections: %s.\n", 
	   s->realm.serverPid, strerror(errno));

	    Do_log_error(error_msg);
            free((void *)client_ptr);
            return SOCKET_ACCEPT_ERROR; 
        }

	    /* log the connection */
	sprintf(error_msg, "New connection on socket %d.\n",
		client_ptr->socket);

	Do_log(SERVER_LOG, error_msg);

	    /* set the socket variables */
	client_ptr->socket_up = TRUE;
	client_ptr->out_buffer_size = 0;
	client_ptr->in_buffer_size = 0;
        client_ptr->date_connected = time(NULL);
	client_ptr->run_level = SIGNING_IN;
	client_ptr->machineID = 0;
	client_ptr->events = NULL;
	client_ptr->channel = 1;
	client_ptr->timeout = 120;
	client_ptr->timeoutFlag = 0;
	client_ptr->broadcast = FALSE;
	client_ptr->stuck = FALSE;
        client_ptr->knightEnergy = 0;
        client_ptr->knightQuickness = 0;
        client_ptr->ageCount = 0;
        client_ptr->morgothCount = 0;
      	client_ptr->suspended = FALSE;
	client_ptr->accountLoaded = FALSE;
	client_ptr->characterLoaded = FALSE;
	client_ptr->characterAnnounced = FALSE;
	client_ptr->muteUntil = 0;
	client_ptr->tagUntil = 0;
	client_ptr->hearBroadcasts = TRUE;
	client_ptr->swearCount = 0;
	client_ptr->battle.rounds = 0;
	client_ptr->battle.timeouts = 0;
	client_ptr->previousName[0] = '\0';
	client_ptr->wizaccount[0] = '\0';
	client_ptr->wizIP[0] = '\0';

	for (terms = 0; terms < 10; terms++) {
	    client_ptr->chatTimes[terms] = 0;
            client_ptr->chatLength[terms] = 0;
	}


            /* set the from and name fields */
            /* determine the dns entry of this connection */
	    /* I've had major problems getting the reentrant fuction
	    working with linux as it is not standard.  I hope the normal
	    function run in main process is okay */
	host_info = NULL;
	theError = 0;
/*
        gethostbyaddr_r((char *)&client_ptr->address.sin_addr,
                sizeof(client_ptr->address.sin_addr), AF_INET, &host_buffer,
		gethost_buffer, 16384, &host_info, &theError);
*/

/*
	errno = 0;
	host_info = gethostbyaddr((char *)&client_ptr->address.sin_addr, 
		sizeof(client_ptr->address.sin_addr), AF_INET);
	theError = errno;
*/

            /* if we received host information */
        if (host_info != NULL) {

                /* copy over the hostname */
            strncpy(&client_ptr->IP, host_info->h_name, SZ_FROM - 1);
	    client_ptr->IP[SZ_FROM - 1] = '\0';

	        /* determine the network address of this connection */
	    string_ptr = client_ptr->IP;

	        /* skip the first term (assume host name) */
	    while (*string_ptr != '\0' && *string_ptr++ != '.') {}

		/* if we found a null, there is no network (local machine) */
	    if (*string_ptr == '\0') {
                strncpy(client_ptr->network, client_ptr->IP, SZ_FROM - 1);
		client_ptr->network[SZ_FROM - 1] = '\0';
		client_ptr->addressResolved = FALSE;
	    }
	    else {
	            /* count the number or remaining terms */
	        terms = 1;
	        string_ptr_two = string_ptr;
	        while (*string_ptr_two != '\0') {

	            if (*string_ptr_two++ == '.')
		        ++terms;
	        }

	            /* remove terms until we find one without numbers or hex or
	            we have only two terms left */

	        string_ptr_two = string_ptr;
	        while (*string_ptr != '\0' || terms > 2) {

	            if (isxdigit(*string_ptr)) {
		        ++string_ptr;
	            }
	            else if (*string_ptr == '.') {
		        string_ptr_two = ++string_ptr;
	            }
	            else {
		        break;
	            }
	        }

	            /* put this shortened hostname into place */
                strncpy(client_ptr->network, string_ptr_two, SZ_FROM - 1);
		client_ptr->network[SZ_FROM - 1];
		client_ptr->addressResolved = TRUE;
	    }
	}
	else {
	        /* use the IP address */
            string_ptr = (char *) inet_ntoa(client_ptr->address.sin_addr);
            strncpy(client_ptr->IP, string_ptr, SZ_FROM - 1);
	    client_ptr->IP[SZ_FROM - 1] = '\0';

		/* get the class C network address */
	    theNetwork.s_addr = client_ptr->address.sin_addr.s_addr &
		    0x00FFFFFF;

	    client_ptr->addressResolved = FALSE;

            string_ptr = (char *) inet_ntoa(theNetwork);
            strncpy(client_ptr->network, string_ptr, SZ_FROM - 1);
	    client_ptr->network[SZ_FROM - 1] = '\0';

                /* stop logging this an an error - too common */
/*
            sprintf(error_msg,
       "[%s:?] gethostbyaddress returned error %d in Do_accept_connections.\n",
       client_ptr->IP, theError);

            Do_log_error(error_msg);
*/
        }

sprintf(error_msg, "Connection, IP=%s, Network=%s.\n", client_ptr->IP, client_ptr->network);

Do_log(DEBUG_LOG, error_msg);

	    /* create a new game object */
        game_ptr = (struct game_t *) Do_malloc(SZ_GAME);

	    /* set up the game variables */
        game_ptr->cleanup_thread = FALSE;
        game_ptr->virtual = FALSE;
        game_ptr->hearAllChannels = HEAR_SELF;
        game_ptr->chatFilter = TRUE;
        game_ptr->sendEvents = FALSE;
        game_ptr->the_socket = client_ptr->socket;
        game_ptr->description = NULL;
	game_ptr->it_combat = NULL;
        game_ptr->events_in = NULL;
	game_ptr->account[0] = '\0';
	strcpy(game_ptr->IP, client_ptr->IP);
	strcpy(game_ptr->network, client_ptr->network);
	game_ptr->machineID = 0;

	    /* initialize the event queue locks */
        Do_init_mutex(&game_ptr->events_in_lock);

	    /* lock the game list */
	Do_lock_mutex(&s->realm.realm_lock);

	    /* put the temp game into the list of games */
        game_ptr->next_game = s->realm.games;
        s->realm.games = game_ptr;

	    /* unlock the game list */
	Do_unlock_mutex(&s->realm.realm_lock);

	    /* init the pthread_att strcture */
	theError = pthread_attr_init(&thread_attr);
	if (theError) {

            sprintf(error_msg, "[0.0.0.0:%d] init of pthread_attr_t failed with error code of %d in Do_accept_connections.\n", s->realm.serverPid, theError);

	    Do_log_error(error_msg);
            free((void *)client_ptr);
            return PTHREAD_ATTR_ERROR;
	}
/*
	pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
	pthread_attr_setstacksize(&thread_attr, 0xfffffff);
*/

	    /* set up all information to be passed to the thread */
        client_ptr->realm = &s->realm;
        client_ptr->game = game_ptr;

	    /* create the new thread */
        theError = pthread_create(&game_ptr->the_thread, NULL,
		Do_start_thread, (void *)client_ptr);

        if (theError) {

            sprintf(error_msg, "[0.0.0.0:%d] thread creation failed with an error code of %d in Do_accept_connections.\n", s->realm.serverPid, theError);

	    Do_log_error(error_msg);
            free((void *)client_ptr);
            return PTHREAD_CREATE_ERROR;
        }

        ++s->num_games;
    }

    return 0;
}


/************************************************************************
/
/ FUNCTION NAME: Do_check_socket(int the_socket)
/
/ FUNCTION: Checks for data waiting on the socket
/
/ AUTHOR: Brian Kelly, 4/23/99
/
/ ARGUMENTS:
/       int the_socket - the socket to check for data
/
/ RETURN VALUE: 
/	bool - true if there is data waiting on the socket
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

int Do_check_socket(int the_socket)
{
    fd_set rmask;
    static struct timeval timeout = { 0, 0 };	/* no timeout */
    char error_msg[SZ_ERROR_MESSAGE];
    int error;

	/* set rmask to check our socket */
    FD_ZERO(&rmask);
    FD_SET(the_socket, &rmask);

	/* check for connections to be accepted */
    error = select(the_socket + 1, &rmask, 0, 0, &timeout);
    if (error < 0) {

        sprintf(error_msg, "[0.0.0.0:?] select on socket failed with error code of %d in Do_check_socket\n", error);

	Do_log_error(error_msg);
        exit(SOCKET_SELECT_ERROR);
    }

        /* if select found no matches, return */
    if (error == 0) {
        return FALSE;
    }

        /* if our socket flag is not set, something is wrong */
    if (!FD_ISSET(the_socket, &rmask)) {

        sprintf(error_msg, "[0.0.0.0:?] select returned %d, but socket flag is off in Do_check_socket.\n", error);

	Do_log_error(error_msg);
        exit (SOCKET_SELECT_ERROR); 
    }
    return TRUE;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_buffer(int the_socket)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 4/23/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_buffer(struct client_t *c)
{
/*
    char error_msg[SZ_ERROR_MESSAGE];
*/
    char error_msg[2048];
    int bytes_sent;

    if (c->socket_up && c->out_buffer_size > 0) {

	    /* send off the data */
        errno = 0;
        c->out_buffer[c->out_buffer_size] = '\0';

#ifdef SEND_DEBUG
        sprintf(error_msg, "[%s] sending %d bytes\n", c->connection_id,
		c->out_buffer_size);

	Do_log(DEBUG_LOG, error_msg);
#endif

#ifdef SEND_PACKET_DEBUG
        sprintf(error_msg, "[%s] (%s)\n", c->connection_id, c->out_buffer);
	Do_log(DEBUG_LOG, error_msg);
#endif


        bytes_sent = send(c->socket, c->out_buffer, c->out_buffer_size, 0);

        if (bytes_sent != c->out_buffer_size) {

            sprintf(error_msg,
	"[%s] send on socket sent %d out of %d bytes in Do_send_buffer: %s.\n",
	c->connection_id, bytes_sent, c->out_buffer_size, strerror(errno));

	    Do_log_error(error_msg);
	    c->socket_up = FALSE;

            sprintf(error_msg, "[%s] Error on socket while sending.\n",
		    c->connection_id);

            Do_log(CONNECTION_LOG, error_msg);

        }
        c->out_buffer_size = 0;
    }

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_out(struct client_t *c, void *the_data, size_t the_size)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 5/6/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_out(struct client_t *c, void *the_data, size_t the_size)
{
    char error_msg[SZ_ERROR_MESSAGE];
    char string_buffer[SZ_LINE], string_buffer2[SZ_LINE];

    if (c->socket_up) {

        if (the_size > SZ_OUT_BUFFER) {

            sprintf(error_msg,
		"[%s] buffer_out overflow with %d bytes in Do_send_out.\n",
		c->connection_id, the_size);

	    Do_log_error(error_msg);
	    Do_send_error(c, error_msg);
            c->socket_up = FALSE;
	    return;
        }

        if (the_size + c->out_buffer_size > SZ_OUT_BUFFER) {
            Do_send_buffer(c);

	    if (the_size + c->out_buffer_size > SZ_OUT_BUFFER) {
		return;
	    }
        }

#ifdef SEND_QUEUE_DEBUG
        memcpy(string_buffer, the_data, the_size);

	    /* remove the "\n" */
	string_buffer[the_size - 1] = '\0';

       	sprintf(string_buffer2, "[%s] Queued (%s)\n", c->connection_id,
		string_buffer);

	Do_log(DEBUG_LOG, string_buffer2);
#endif

        memcpy(&c->out_buffer[c->out_buffer_size], the_data, the_size);
        c->out_buffer_size += the_size;

    }

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_string(struct client_t *c, char *theMessage)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 8/11/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_string(struct client_t *c, char *theMessage)
{
    int theSize;

	/* determine the string size */
    theSize = strlen(theMessage);

	/* send the data */
    Do_send_out(c, theMessage, theSize);

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_double(struct client_t *c, double theDouble)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 8/13/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_double(struct client_t *c, double theDouble)
{
    char tmpDouble[SZ_NUMBER];

	/* determine the string size */
    sprintf(tmpDouble, "%0.lf\n", theDouble);

	/* send the data */
    Do_send_string(c, tmpDouble);

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_float(struct client_t *c, float theFloat)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 8/13/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_float(struct client_t *c, double theFloat)
{
    char tmpFloat[SZ_NUMBER];

	/* determine the string size */
    sprintf(tmpFloat, "%0.lf\n", theFloat);

	/* send the data */
    Do_send_string(c, tmpFloat);

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_fpfloat(struct client_t *c, float theFloat)
/
/ FUNCTION: Send a full-precision float to the client
/
/ AUTHOR: Brian Kelly, 8/25/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_fpfloat(struct client_t *c, double theFloat)
{
    char tmpFloat[SZ_NUMBER];

	/* determine the string size */
    sprintf(tmpFloat, "%.4lf\n", theFloat);

	/* send the data */
    Do_send_string(c, tmpFloat);

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_int(struct client_t *c, int theInt)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 8/13/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_int(struct client_t *c, int theInt)
{
    char tmpInt[SZ_NUMBER];

	/* determine the string size */
    sprintf(tmpInt, "%d\n", theInt);

	/* send the data */
    Do_send_string(c, tmpInt);

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_short(struct client_t *c, short theShort)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 8/13/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_short(struct client_t *c, short theShort)
{
    char tmpShort[SZ_NUMBER];

	/* determine the string size */
    sprintf(tmpShort, "%hd\n", theShort);

	/* send the data */
    Do_send_string(c, tmpShort);

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_bool(struct client_t *c, bool theBool)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 8/13/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_bool(struct client_t *c, short theBool)
{
    if (theBool == FALSE)

	    /* send the data */
        Do_send_string(c, "No\n");
    else

	    /* send the data */
        Do_send_string(c, "Yes\n");

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_send_error(struct client_t *c, char *theError)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 10/2/99
/
/ ARGUMENTS:
/	int the_socket - the socket to send the information on
/	size_t the_size - the number of byest to send
/	void *the_data - a pointer to the data to be sent
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_send_error(struct client_t *c, char *theError)
{
    Do_send_int(c, ERROR_PACKET);
    Do_send_string(c, theError);
    Do_send_buffer(c);
    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_read_socket(struct client_t *c)
/
/ FUNCTION: Send data over the socket to the player
/
/ AUTHOR: Brian Kelly, 4/24/99
/
/ ARGUMENTS:
/       int the_socket - the socket to read data from
/	size_t the_size - the number of bytes to read from the socket
/	void *the_data - a pointer to the data to be read
/
/ RETURN VALUE: none
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/       This routine is specially designed to:
/
*************************************************************************/

Do_read_socket(struct client_t *c)
{
    char error_msg[SZ_ERROR_MESSAGE];
    int bytes_read;
    struct event_t *event_ptr;

    errno = 0;

	/* read the data from the socket */
    bytes_read = recv(c->socket, &c->in_buffer[c->in_buffer_size],
	    (SZ_IN_BUFFER - c->in_buffer_size), 0);

    if (bytes_read <= 0) {

	    /* the socket is no longer any good */
	c->socket_up = FALSE;

	    /* if client closed abruptly, the connection will be reset */
	if (errno = ECONNRESET) {

		/* Error too common for the error log */
/*
	    sprintf(error_msg,
	           "[%s] Received a ECONNRESET on socket in Do_read_socket.\n",
		   c->connection_id);

            Do_log_error(error_msg);
*/
	}
	else {

            sprintf(error_msg,
	       "[%s] read on socket returned %d bytes in Do_read_socket: %s\n",
	       c->connection_id, bytes_read, strerror(errno));

            Do_log_error(error_msg);
	}

	sprintf(error_msg, "[%s] Received an error on the socket.\n",
		c->connection_id);

	Do_log(CONNECTION_LOG, error_msg);
    }

    else if (bytes_read + c->in_buffer_size > SZ_IN_BUFFER) {

	    /* the socket is no longer any good */
	c->socket_up = FALSE;

	    /* log an error */
        sprintf(error_msg, "[%s] added %d to %d bytes for in_buffer overvlow in Do_read_socket.\n", c->connection_id, bytes_read, c->in_buffer_size);

	Do_log_error(error_msg);
    }

    else {
	c->in_buffer_size += bytes_read;
    }

    return;
}


/************************************************************************
/
/ FUNCTION NAME: Do_get_socket_string(struct client_t *c, void *the_data, size_t the_size)
/
/ FUNCTION: reads the next packet type off the socket
/
/ AUTHOR: Brian Kelly, 4/23/99
/
/ ARGUMENTS:
/	int the_socket - the socket to read the packet type
/
/ RETURN VALUE: 
/	int - the type of packet next on the socket
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

int Do_get_socket_string(struct client_t *c, char *theString, size_t theSize)
{
    size_t theLength;
    char string_buffer[SZ_LINE], error_msg[SZ_ERROR_MESSAGE + SZ_OUT_BUFFER];
    sigset_t sigMask;
    int theSignal;

	/* prepare to unblock SIGIO */
    sigemptyset(&sigMask);
    sigaddset(&sigMask, SIGIO);
    sigaddset(&sigMask, SIGALRM);

    for (;;) {

	    /* if the socket is down, return an error */
	if (!c->socket_up) {
	    return S_ERROR;
	}

	    /* if we have information in the buffer */
	if (c->in_buffer_size) {

	        /* see if the entire string has been downloaded */
	    theLength = strlen(c->in_buffer);

	    if (theLength < c->in_buffer_size) {

		    /* check that the passed pointer can handle the size */
		if (theSize - 1 < theLength) {

	                /* log the error */
		    sprintf(error_msg, "[%s] Client returned a string of %d bytes, %d max in Do_get_socket_string.\n", c->connection_id, theLength, theSize);

		    Do_log_error(error_msg);

	    		/* get as much of the string as we can */
		    strncpy(theString, c->in_buffer, theSize - 1);
		    theString[theSize - 1] = '\0';
		}
		else {
		    strcpy(theString, c->in_buffer);
		}

		    /* add the terminating null to the string */
		++theLength;

		    /* move up information in the buffer */
	        if (theLength < c->in_buffer_size) {
	            c->in_buffer_size -= theLength;
	            memmove(c->in_buffer, &c->in_buffer[theLength],
			    c->in_buffer_size);
	        }
	        else {
	            c->in_buffer_size = 0;
	        }

#ifdef RECEIVE_DEBUG
	        sprintf(error_msg, "[%s] received %d bytes\n",
			c->connection_id, theLength);

		Do_log(DEBUG_LOG, error_msg);
#endif

#ifdef RECEIVE_PACKET_DEBUG
        	sprintf(error_msg, "[%s] (%s)\n", c->connection_id, theString);
		Do_log(DEBUG_LOG, error_msg);
#endif

	        return S_NORM;
            }

		/* the the buffer is maxed and we're here,
			the string is too large for the buffer */

	    if (c->in_buffer_size == SZ_IN_BUFFER) {

		sprintf(error_msg, "[%s] Request for a string, %d bytes, larger than buffer in Do_get_socket_string.\n", c->connection_id, theLength);

		Do_log_error(error_msg);

		c->socket_up = FALSE;
		return S_ERROR;
	    }
	}

	    /* We need to wait for information, so pause */
	if (c->socket_up) {

#ifdef SUSPEND_DEBUG
	    sprintf(error_msg,
		    "[%s] now sleeping with alarm set for %d seconds.\n",
		    c->connection_id, c->timeout);

	    Do_log(DEBUG_LOG, error_msg);
#endif
	    sigwait(&sigMask, &theSignal);

#ifdef SUSPEND_DEBUG
	    sprintf(error_msg, "[%s] awoken on signal %d.\n",
		    c->connection_id, theSignal);

	    Do_log(DEBUG_LOG, error_msg);
#endif
/*
	    sleep(.1);
*/

	}
	else {
	    theSignal = SIGIO;
	}

	    /* check events and the socket on a SIGIO */
	if (theSignal == SIGIO) {

	        /* if the socket is up, we have room in the buffer and
		        there is info waiting */

            if (c->socket_up && c->in_buffer_size < SZ_IN_BUFFER &&
		    Do_check_socket(c->socket)) {

                Do_read_socket(c);
            }

	        /* see if any other threads have sent us an event */
	    Do_check_events_in(c);
	}

	    /* see if the tread alarm went off */
	else if (theSignal == SIGALRM) {
/*
	if (time(NULL) > c->timeoutAt) {
*/

	    switch(++c->timeoutFlag) {

	        /* alarm has gone off once */
	    case 1:
	        Do_send_int(c, PING_PACKET);
	        Do_send_buffer(c);

		    /* give the client 15 seconds to respond */
	        alarm(15);
		c->timeoutAt = time(NULL) + 15;
	        break;

	        /* gone off twice */
	    case 2:

                sprintf(error_msg, "[%s] Socket connection timed out.\n",
		        c->connection_id);

                Do_log(CONNECTION_LOG, error_msg);
	        Do_send_error(c, "The socket connection timed out.\n");
	        Do_send_buffer(c);

		    /* assume the network connection is down */
	        sprintf(error_msg,
		 "[%s] Socket connection timed out in Do_get_socket_string.\n",
		 c->connection_id);

	        Do_log_error(error_msg);
	        c->socket_up = FALSE;
	        return S_ERROR;
	    }
	}

	    /* unknown signal */
	else {
	    sprintf(error_msg, "[%s] Received unknown signal %d.\n",
		    c->connection_id, theSignal);

	    Do_log_error(error_msg);
	}
    }
}


/************************************************************************
/
/ FUNCTION NAME: Do_get_string(struct client_t *c, char *theString,
/	int maxSize);
/
/ FUNCTION: Wait until char is returned from player
/
/ AUTHOR: Brian Kelly, 8/11/99
/
/ ARGUMENTS:
/	client_t c - the client data strcture
/	size_t the_size - the number of bytes fo data expected
/	void *the_data - a pointer to where the data should be written
/	bool exact - Does there need to be exactly the_size bytes?
/
/ RETURN VALUE: 
/	bool - Was the data or a timeout registered
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

int Do_get_string(struct client_t *c, char *theString, int maxSize)
{
    char packetTmp[SZ_PACKET_TYPE], error_msg[SZ_ERROR_MESSAGE];
    int packetType;
    int returnCode;

    Do_send_buffer(c);

    c->timeoutFlag = 0;
    alarm(c->timeout);
    c->timeoutAt = time(NULL) + c->timeout;

    for(;;) {

	    /* get the header of the next packet */
	errno = 0;
        if (Do_get_socket_string(c, packetTmp, SZ_PACKET_TYPE) == S_ERROR) {
	    alarm(0);
	    return S_ERROR;
	}

	    /* convert the string to an integer */
	packetType = strtol(packetTmp, NULL, 10);

	switch(packetType) {

	    /* if the packet type is returning an answer */
        case C_RESPONSE_PACKET:

	        /* read the string */
	    returnCode = Do_get_socket_string(c, theString, maxSize);
	    alarm(0);
	    return returnCode;

	case C_CANCEL_PACKET:

	    alarm(0);
	    return S_CANCEL;

	case C_PING_PACKET:

	    if (c->timeoutFlag == 1) {

		alarm(0);
	        return S_TIMEOUT;
	    }
	    else {
	        sprintf(error_msg, "[%s] Received unexpected ping packet.\n",
		    c->connection_id);

	        Do_log_error(error_msg);
	    }
	    break;

	default:

	    if (Do_packet(c, packetType) == S_ERROR) {
		alarm(0);
	        return S_ERROR;
	    }
	}
    }
}


/************************************************************************
/
/ FUNCTION NAME: Do_get_double(struct client_t *c, double *theDouble);
/
/ FUNCTION: Wait until char is returned from player
/
/ AUTHOR: Brian Kelly, 8/11/99
/
/ ARGUMENTS:
/	client_t c - the client data strcture
/	size_t the_size - the number of bytes fo data expected
/	void *the_data - a pointer to where the data should be written
/	bool exact - Does there need to be exactly the_size bytes?
/
/ RETURN VALUE: 
/	bool - Was the data or a timeout registered
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

int Do_get_double(struct client_t *c, double *theDouble)
{
    char tmpDouble[SZ_NUMBER];
    int rc;

    if (rc = Do_get_string(c, tmpDouble, SZ_NUMBER) != S_NORM) {
	return rc;
    }

    *theDouble = floor(strtod(tmpDouble, NULL));

        /* insure that the number is finite */
    if (!finite(*theDouble)) {

	    /* fix the number */
        *theDouble = 0;
    }

    return S_NORM;
}


/************************************************************************
/
/ FUNCTION NAME: Do_get_long(struct client_t *c, long *theLong);
/
/ FUNCTION: Wait until char is returned from player
/
/ AUTHOR: Brian Kelly, 10/2/99
/
/ ARGUMENTS:
/	client_t c - the client data strcture
/	size_t the_size - the number of bytes fo data expected
/	void *the_data - a pointer to where the data should be written
/	bool exact - Does there need to be exactly the_size bytes?
/
/ RETURN VALUE: 
/	bool - Was the data or a timeout registered
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

int Do_get_long(struct client_t *c, long *theLong)
{
    char tmpLong[SZ_NUMBER];
    int returnCode;

    returnCode = Do_get_string(c, tmpLong, SZ_NUMBER);

    if (returnCode != S_NORM) {
	return returnCode;
    }

    *theLong = strtol(tmpLong, NULL, 10);

    return S_NORM;
}


/************************************************************************
/
/ FUNCTION NAME: Do_get_nothing(struct client_t *c);
/
/ FUNCTION: Wait until char is returned from player
/
/ AUTHOR: Brian Kelly, 11/3/99
/
/ ARGUMENTS:
/	client_t c - the client data strcture
/	size_t the_size - the number of bytes fo data expected
/	void *the_data - a pointer to where the data should be written
/	bool exact - Does there need to be exactly the_size bytes?
/
/ RETURN VALUE: 
/	bool - Was the data or a timeout registered
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

int Do_get_nothing(struct client_t *c)
{
    char packetTmp[SZ_PACKET_TYPE];
    char error_msg[SZ_ERROR_MESSAGE];
    int packetType;

    for(;;) {

	    /* if the socket is up, we have room in the buffer and
		    there is info waiting */

        if (c->socket_up && c->in_buffer_size < SZ_IN_BUFFER &&
		Do_check_socket(c->socket)) {

            Do_read_socket(c);
        }

	    /* if the socket is up, and the buffer has data */
        if (c->socket_up && c->in_buffer_size) {

	    c->timeoutAt = time(NULL) + c->timeout;

	        /* get the header of the next packet */
            if (Do_get_socket_string(c, packetTmp, SZ_PACKET_TYPE) == S_ERROR) 
	        return S_ERROR;

	        /* convert the string to an integer */
	    packetType = strtol(packetTmp, NULL, 10);

	    switch(packetType) {

	        /* we're not expecting an answer */
            case C_RESPONSE_PACKET:
	    case C_CANCEL_PACKET:
	    case C_PING_PACKET:

	            /* print an error message */
	        sprintf(error_msg,
		"[%s] Unexpected packet type %d returned in Do_get_nothing.\n",
		c->connection_id, packetType);

	        Do_log_error(error_msg);
	        Do_send_error(c, error_msg);
                c->socket_up = FALSE;

	        return S_ERROR;

	    default:

	        if (Do_packet(c, packetType) == S_ERROR) {
	            return S_ERROR;
		}
	    }
	}
	else {
	    return S_NORM;
	}
    }
}


/************************************************************************
/
/ FUNCTION NAME: int Do_packet(client_t *c, char packetType);
/
/ FUNCTION: Wait until char is returned from player
/
/ AUTHOR: Brian Kelly, 8/11/99
/
/ ARGUMENTS:
/	client_t c - the client data strcture
/	size_t the_size - the number of bytes fo data expected
/	void *the_data - a pointer to where the data should be written
/	bool exact - Does there need to be exactly the_size bytes?
/
/ RETURN VALUE: 
/	bool - Was the data or a timeout registered
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

int Do_packet(struct client_t *c, int thePacket)
{
    char numChars;
    time_t timeNow;
    struct event_t *event_ptr;
    char error_msg[SZ_ERROR_MESSAGE];
    char player_name[SZ_NAME];
    char string_buffer[SZ_CHAT - 20];
    long theLong;

	/* switch on the type of event */
    switch(thePacket) {

    case C_CHAT_PACKET:

	    /* get the string to be sent and quit on error*/
	if (Do_get_socket_string(c, string_buffer, SZ_CHAT - 20) == S_ERROR)
	    return S_ERROR;

	    /* mute null chat messages */
	if (strlen(string_buffer) > 0) {

		/* if the player is muted */
	    if (c->muteUntil < time(NULL)) {

                    /* send the chat message */
	        Do_chat(c, string_buffer);
	    }
	}

	/* get rid of old tags */
	if(c->tagUntil < time(NULL))
	{
		Do_remove_prefix_suffix(c);
		/* if(c->characterLoaded)
		{
			strcpy(string_buffer, c->player.name);
			if(c->characterAnnounced)
			{
				Do_send_specification(c, REMOVE_PLAYER_EVENT);
			}
			strncpy(c->modifiedName, string_buffer, SZ_NAME - 1);
			if(c->characterAnnounced)
			{
				Do_send_specification(c, ADD_PLAYER_EVENT);
				Do_name(c);
			}
		} */
	}

	    /* finished */
	return S_NORM;

    case C_EXAMINE_PACKET:

	    /* read player name and quit if there's an error */
	if (Do_get_socket_string(c, player_name, SZ_NAME) == S_ERROR)
	    return S_ERROR;

	    /* request the info from the player */
	event_ptr = (struct event_t *) Do_create_event();
	event_ptr->type = REQUEST_RECORD_EVENT;
	event_ptr->from = c->game;

	if (!Do_send_character_event(c, event_ptr, player_name)) {
	    free((void *)event_ptr);
	}

	    /* finished */
	return S_NORM;

    case C_SCOREBOARD_PACKET:

	    /* read starting record and quit if there's an error */
	if (Do_get_socket_string(c, string_buffer, SZ_NAME) == S_ERROR)
	    return S_ERROR;

	theLong = strtol(string_buffer, NULL, 10);
	    /* convert the string to an int */
	Do_scoreboard(c, (int) theLong, TRUE);

	    /* finished */
	return S_NORM;

    case C_ERROR_PACKET:

	    /* see if the client included a message */
	if (Do_get_socket_string(c, error_msg, SZ_ERROR_MESSAGE) !=
		S_NORM) {

	    error_msg[0] = '\0';
	}

	    /* log the error */
	sprintf(string_buffer,
		"[%s] Client returned an error packet in Do_packet: %s.\n",
		c->connection_id, error_msg);

	Do_log_error(string_buffer);

	c->socket_up = FALSE;
	return S_ERROR;

    default:

	    /* print an error message */
	sprintf(error_msg,
		"[%s] Received unknown packet type %d in Do_packet.\n",
		c->connection_id, thePacket);

	Do_log_error(error_msg);
	Do_send_error(c, error_msg);
        c->socket_up = FALSE;

	    /* exit gracefully */
	return S_ERROR;
    }
}


/************************************************************************
/
/ FUNCTION NAME: int Do_get_network_address(client_t *c, int hostAddress, int subnetMask);
/
/ FUNCTION: Wait until char is returned from player
/
/ AUTHOR: Brian Kelly, 6/8/00
/
/ ARGUMENTS:
/	client_t c - the client data strcture
/	size_t the_size - the number of bytes fo data expected
/	void *the_data - a pointer to where the data should be written
/	bool exact - Does there need to be exactly the_size bytes?
/
/ RETURN VALUE: 
/	bool - Was the data or a timeout registered
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

Do_get_network_address(struct in_addr *hostAddress, int subnetMask)
{
    unsigned int bitMask;

	/* create a bitmask.  Ex: 8 mask = 0xffffff00 */
    bitMask = pow(2, subnetMask) - 1;
    bitMask = (unsigned int) 0xffffffff ^ bitMask;

	/* return only the network portion of the hostAddress */
    hostAddress->s_addr = bitMask & htonl(hostAddress->s_addr);
    hostAddress->s_addr = ntohl(hostAddress->s_addr);
    return;
}


/************************************************************************
/
/ FUNCTION NAME: int Do_check_host_address(int hostAddress, int networkAddress, int subnetMask);
/
/ FUNCTION: Wait until char is returned from player
/
/ AUTHOR: Brian Kelly, 6/8/00
/
/ ARGUMENTS:
/	client_t c - the client data strcture
/	size_t the_size - the number of bytes fo data expected
/	void *the_data - a pointer to where the data should be written
/	bool exact - Does there need to be exactly the_size bytes?
/
/ RETURN VALUE: 
/	bool - Was the data or a timeout registered
/
/ MODULES CALLED: wmove(), _filbuf(), clearok(), waddstr(), wrefresh(),
/       wclrtoeol()
/
/ DESCRIPTION:
/       Read a string from the keyboard.
/
/************************************************************************/

int Do_check_host_address(struct in_addr *hostAddress, struct in_addr *networkAddress, int subnetMask)
{
    struct in_addr checkAddress;

    if (subnetMask < 0 || subnetMask > 24) {
	return FALSE;
    }

    checkAddress.s_addr = hostAddress->s_addr;

    Do_get_network_address(&checkAddress, subnetMask);

    if (networkAddress->s_addr == checkAddress.s_addr) {
        return TRUE;
    }
    
    return FALSE;
}