/
Sapphire/bin/
Sapphire/db/
Sapphire/db/OLC_rooms/
Sapphire/db/abi/
Sapphire/db/em_src/
Sapphire/db/helps/
Sapphire/db/helps/emman/ifunc/
Sapphire/db/npcs/Tatt/
Sapphire/db/objects/Tatt/
Sapphire/db/q_data/
Sapphire/db/rooms/Tatt/
Sapphire/doc/
Sapphire/doc/em/
Sapphire/etc/
Sapphire/src/abic/
Sapphire/src/areacon/
Sapphire/src/client/
Sapphire/src/embc/
Sapphire/src/emi/
Sapphire/src/emi/test/
Sapphire/src/include/
Sapphire/src/sapphire/em/
Sapphire/src/tcon/
/*
 * Copyright (C) 1995-1997 Christopher D. Granz
 *
 * This header may not be removed.
 *
 * Refer to the file "License" included in this package for further
 * information and before using any of the following.
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
#include <time.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/telnet.h>
#include <arpa/inet.h>

#include "sapphire.h"


/*
 * Functions
 */

/*
 * Init the MUD-comm socket.
 */
void init_mudcomm_socket( int iPort )
{
    struct sockaddr_in sSA;
    int iX = 1;

    if ( ( sMUDCommControl = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
        wcfatal( "Create socket: %s.", strerror( errno ) );

    if ( setsockopt( sMUDCommControl, SOL_SOCKET, SO_REUSEADDR,
      (char *) &iX, sizeof( iX ) ) < 0 )
        sap_fatal( "Set socket option: %s.", strerror( errno ) );

    zero_out( &sSA, sizeof( sSA ) );
    sSA.sin_family = AF_INET;
    sSA.sin_port   = htons( iPort );

    if ( bind( sMUDCommControl, (struct sockaddr *) &sSA, sizeof( sSA ) )
      < 0 )
        sap_fatal( "Bind: %s.", strerror( errno ) );

    if ( listen( sMUDCommControl, 3 ) < 0 )
        sap_fatal( "Listen: %s.", strerror( errno ) );
}


void accept_mudcomm_connection( void )
{
    MUD_COMM_DATA *pMUDComm;
    struct sockaddr_in sSock;
    struct hostent *pHost;
    char cBuf[MAX_STRING];
    char *pBuf;
    int iSize;
    sapsocket sSocket;

    iSize   = sizeof( sSock );
    sSocket = accept( sMUDCommControl, (struct sockaddr *) &sSock,
                &iSize );

    if ( sSocket < 0 )
    {
        sap_error( "Accept: %s.", strerror( errno ) );
        return;
    }

    if ( fcntl( sSocket, F_SETFL, FNDELAY ) < 0 )
    {
        sap_error( "Set descriptor option: %s.", strerror( errno ) );
        close( sSocket );
        return;
    }

    if ( getpeername( sSocket, (struct sockaddr *) &sSock, &iSize ) < 0 )
    {
        /* We MUST know the peer name for MUD-Comm connections! */
        sap_error( "Get peer name: %s.", strerror( errno ) );
        close( sSocket );
        return;
    }

    pMUDComm                 = alloc_mem( sizeof( *pMUDComm ) );

    pMUDComm->sMUDCommSocket = sSocket;
    pMUDComm->pMUDName       = EMPTY_STRING;
    pMUDComm->iConState      = MUD_COMM_CON_IDENT;
    pMUDComm->iMaxOutBufSize = 2048;
    pMUDComm->iMaxInBufSize  = 4096;
    pMUDComm->pOutBuffer     = alloc_mem( pMUDComm->iMaxOutBufSize );
    pMUDComm->pInBuffer      = alloc_mem( pMUDComm->iMaxInBufSize );

    pBuf                     = inet_ntoa( sSock.sin_addr );
    pHost               = gethostbyaddr( (char *) &sSock.sin_addr.s_addr,
                            sizeof( sSock.sin_addr.s_addr ), AF_INET );
    pMUDComm->pHostname = str_dup( ( pHost ? pHost->h_name : pBuf ) );

    snprintf( cBuf, MAX_STRING,
      "\n  [MUD-Comm] Connection established with: %s",
      pMUDComm->pHostname );

    if ( pHost != NULL )
    {
        strcat( cBuf, "\n                                          (" );
        strcat( cBuf, pBuf );
        strcat( cBuf, ")" );
    }

    lprintf( cBuf );

    pMUDComm->pNext          = pMUDCommList;
    pMUDCommList             = pMUDComm;
}


int make_mudcomm_connection( char *pMUDName_, char *pHostname, int iPort )
{
    struct sockaddr_in siName;
    struct hostent *pHost;
    MUD_COMM_DATA *pMUDComm;
    sapsocket sSocket;

    if ( ( pHost = gethostbyname( pHostname ) ) == NULL )
    {
        long lAddr;

        lAddr = inet_addr( pHostname );

        if ( ( pHost = gethostbyaddr( (char *) &lAddr,
          sizeof( struct in_addr ), AF_INET ) ) == NULL )
        {
            sap_error( "Get host: %s.", strerror( errno ) );
            return ( -1 );
        }
    }

    if ( ( sSocket = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    {
        sap_error( "Create socket: %s.", strerror( errno ) );
        return ( -2 );
    }

    siName.sin_family     = AF_INET;
    siName.sin_port       = htons( iPort );
    memcpy( &siName.sin_addr.s_addr, pHost->h_addr, pHost->h_length );

    if ( connect( sSocket, (struct sockaddr *) &siName, sizeof( siName ) )
      < 0 )
        return ( -3 );

    if ( fcntl( sSocket, F_SETFL, O_NONBLOCK ) < 0 )
    {
        sap_error( "Set descriptor option: %s.", strerror( errno ) );
        close( sSocket );
        return ( -4 );
    }

    pMUDComm                 = alloc_mem( sizeof( *pMUDComm ) );

    pMUDComm->sMUDCommSocket = sSocket;
    pMUDComm->pHostname      = str_dup( pHostname );
    pMUDComm->pMUDName       = str_dup( pMUDName_ );
    pMUDComm->iConState      = MUD_COMM_CON_READY;
    pMUDComm->iMaxOutBufSize = 2048;
    pMUDComm->iMaxInBufSize  = 4096;
    pMUDComm->pOutBuffer     = alloc_mem( pMUDComm->iMaxOutBufSize );
    pMUDComm->pInBuffer      = alloc_mem( pMUDComm->iMaxInBufSize );

    lprintf( "\n  [MUD-Comm] Connection established with: %s %d",
      pMUDComm->pHostname, iPort );

    pMUDComm->pNext          = pMUDCommList;
    pMUDCommList             = pMUDComm;

    /*
     * Send our name to the remote MUD.
     */
    write_mudcomm_data_buffer( pMUDComm, pMUDName,
      ( strlen( pMUDName ) + 1 ) );

    return ( 0 );
}


void close_mudcomm_connection( MUD_COMM_DATA **ppMUDComm )
{
    MUD_COMM_DATA *pMUDComm  = *ppMUDComm;
    MUD_COMM_DATA *pMUDComm2;
    GENERIC_DATA *pGen;
    GENERIC_DATA *pGenNext;

    if ( pMUDComm == pMUDCommList )
        pMUDCommList         = pMUDComm->pNext;
    else
    {
        for ( pMUDComm2 = pMUDCommList; pMUDComm2 != NULL
          && pMUDComm2->pNext != pMUDComm; pMUDComm2 = pMUDComm2->pNext );

        pMUDComm2->pNext     = pMUDComm->pNext;
    }

    lprintf( "\n  Closing [MUD-Comm] connection to: %s",
      pMUDComm->pHostname );

    close( pMUDComm->sMUDCommSocket );

    for ( pGen = pMUDComm->pWaitList; pGen; pGen = pGenNext )
    {
        pGenNext             = pGen->pNext;
        free_mem( (void **) &pGen );
    }

    free_mem( (void **) &pMUDComm->pOutBuffer );
    free_mem( (void **) &pMUDComm->pInBuffer );

    str_free( pMUDComm->pHostname );
    str_free( pMUDComm->pMUDName );
    free_mem( (void **) &pMUDComm );

    *ppMUDComm               = NULL;
}


bool read_mudcomm_data( MUD_COMM_DATA *pMUDComm )
{
    int i, iRead         = 0;

    i                    = pMUDComm->iInBufSize;

    if ( i >= ( pMUDComm->iMaxInBufSize - 1 ) )
    {
        lprintf( "%s input overflow!", pMUDComm->pHostname );
        return ( FALSE );
    }

    for ( ; ; )
    {
        iRead            = read( pMUDComm->sMUDCommSocket,
                             ( pMUDComm->pInBuffer + i ),
                             ( pMUDComm->iMaxInBufSize - i - 1 ) );

        if ( iRead > 0 )
            i           += iRead;
        else if ( iRead == 0 )
            return ( FALSE );
        else if ( errno == EAGAIN /* EWOULDBLOCK */ )
            break;
        else if ( errno == EINTR )
            continue;
        else
        {
            /* sap_error( "Read: %s.", strerror( errno ) ); */
            return ( FALSE );
        }
    }

    pMUDComm->iInBufSize = i;
    return ( TRUE );
}


int read_mudcomm_data_buffer( MUD_COMM_DATA *pMUDComm, byte *pOutput,
                              long lSize )
{
    long l = MIN( pMUDComm->iInBufSize, lSize );

    memcpy( pOutput, pMUDComm->pInBuffer, l );
    memcpy( pMUDComm->pInBuffer, &pMUDComm->pInBuffer[l],
      ( l - pMUDComm->iInBufSize ) );

    return ( l );
}


bool write_mudcomm_data( MUD_COMM_DATA *pMUDComm )
{
    do
    {
        errno             = 0;
        write( pMUDComm->sMUDCommSocket, pMUDComm->pOutBuffer,
          pMUDComm->iOutBufSize );
    }
    while ( errno == EINTR );

    if ( errno != 0 )
    {
        sap_error( "Write: %s.", strerror( errno ) );
        return ( FALSE );
    }

    return ( TRUE );
}


void write_mudcomm_data_buffer( MUD_COMM_DATA *pMUDComm, byte *pData,
                                long lSize )
{
    /*
     * Expand the buffer as needed.
     */
    while ( ( pMUDComm->iOutBufSize + lSize ) >= pMUDComm->iMaxOutBufSize )
    {
        long lNewSize;

        if ( pMUDComm->iMaxOutBufSize > 32768 )
        {
            sap_error( "MUD-Comm buffer: Overflow; closing." );
            close_mudcomm_connection( &pMUDComm );
            return;
        }

        lNewSize = ( pMUDComm->iMaxOutBufSize << 1 ); /* x 2 */
        pMUDComm->pOutBuffer     = realloc_mem( pMUDComm->pOutBuffer,
                                     lNewSize );
        pMUDComm->iMaxOutBufSize = lNewSize;
    }

    memcpy( ( pMUDComm->pOutBuffer + pMUDComm->iOutBufSize ), pData,
      lSize );
    pMUDComm->iOutBufSize       += lSize;
}


bool process_mudcomm_output( MUD_COMM_DATA *pMUDComm )
{
    if ( pMUDComm->iOutBufSize == 0 )
        return ( TRUE );

    if ( write_mudcomm_data( pMUDComm ) != TRUE )
        return ( FALSE );
    else
    {
        pMUDComm->iOutBufSize = 0;
        return ( TRUE );
    }
}


/*
 * Deal with MUD_COMM_CMD_WIZ_CHANNEL commands.
 */
void mudcomm_cmd_wiz_channel( MUD_COMM_DATA *pMUDComm )
{
    TERM_DATA *pTerm;
    char cBuf[MAX_STRING];
    char cBuf2[MAX_STRING];
    char cBuf3[MAX_STRING];
    int i;
    int iPos = 1;

    for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
      && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
        cBuf2[i] = pMUDComm->pInBuffer[iPos++];

    cBuf2[i] = '\0';

    if ( ++iPos >= pMUDComm->iInBufSize )
        return;

    for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
      && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
        cBuf3[i] = pMUDComm->pInBuffer[iPos++];

    cBuf3[i] = '\0';

    snprintf( cBuf, MAX_STRING, "\n\r[WizComm] %s@%s : %s\n\r", cBuf2,
      pMUDComm->pMUDName, cBuf3 );

    for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
    {
        if ( ( pTerm->iConState == CON_PLAYING
          || pTerm->iConState == CON_NPC_EDITOR
          || pTerm->iConState == CON_OBJECT_EDITOR
          || pTerm->iConState == CON_ROOM_EDITOR )
          && pTerm->pChar != NULL
          && !IS_SET( pTerm->pChar->pPCData->fChanFlags,
          FLAG_CHANNEL_NO_WIZCOMM )
          && pTerm->pChar->iLevel >= BUILDER_LEVEL )
            write_string_buffer( pTerm, cBuf );
    }
}


void mudcomm_cmd_chat_channel( MUD_COMM_DATA *pMUDComm )
{
    TERM_DATA *pTerm;
    char cBuf[MAX_STRING];
    char cBuf2[MAX_STRING];
    char cBuf3[MAX_STRING];
    int i;
    int iPos = 1;

    for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
      && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
        cBuf2[i] = pMUDComm->pInBuffer[iPos++];

    cBuf2[i] = '\0';

    if ( ++iPos >= pMUDComm->iInBufSize )
        return;

    for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
      && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
        cBuf3[i] = pMUDComm->pInBuffer[iPos++];

    cBuf3[i] = '\0';

    snprintf( cBuf, MAX_STRING, "\n\r[Chat] %s@%s : %s\n\r", cBuf2,
      pMUDComm->pMUDName, cBuf3 );

    if ( iMode == MODE_RPG )
    {
        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
        {
            if ( ( pTerm->iConState == CON_PLAYING
              || pTerm->iConState == CON_NPC_EDITOR
              || pTerm->iConState == CON_OBJECT_EDITOR
              || pTerm->iConState == CON_ROOM_EDITOR )
              && pTerm->pChar != NULL
              && !IS_SET( pTerm->pChar->pPCData->fChanFlags,
              FLAG_CHANNEL_NO_CHAT )
              && pTerm->pChar->iLevel >= BUILDER_LEVEL )
                write_string_buffer( pTerm, cBuf );
        }
    }
    else
    {
        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
        {
            if ( ( pTerm->iConState == CON_PLAYING
              || pTerm->iConState == CON_NPC_EDITOR
              || pTerm->iConState == CON_OBJECT_EDITOR
              || pTerm->iConState == CON_ROOM_EDITOR )
              && !IS_SET( pTerm->pChar->pPCData->fChanFlags,
              FLAG_CHANNEL_NO_CHAT )
              && pTerm->pChar != NULL )
                write_string_buffer( pTerm, cBuf );
        }
    }
}


void mudcomm_cmd_tell_channel( MUD_COMM_DATA *pMUDComm )
{
    TERM_DATA *pTerm;
    char cBuf[MAX_STRING];
    char cBuf2[MAX_STRING];
    char cBuf3[MAX_STRING];
    char cBuf4[MAX_STRING];
    int i;
    int iPos = 1;
    byte b;
    byte b2;

    for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
      && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
        cBuf2[i] = pMUDComm->pInBuffer[iPos++];

    cBuf2[i] = '\0';

    if ( ( iPos += 3 ) >= pMUDComm->iInBufSize )
        return;

    for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
      && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
        cBuf3[i] = pMUDComm->pInBuffer[iPos++];

    cBuf3[i] = '\0';

    if ( ++iPos >= pMUDComm->iInBufSize )
        return;

    for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
      && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
        cBuf4[i] = pMUDComm->pInBuffer[iPos++];

    cBuf4[i] = '\0';

    snprintf( cBuf, MAX_STRING, "\n\r%s@%s tells you `%s'\n\r",
      cBuf3, pMUDComm->pMUDName, cBuf4 );

    if ( iMode == MODE_RPG )
    {
        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
        {
            if ( ( pTerm->iConState == CON_PLAYING
              || pTerm->iConState == CON_NPC_EDITOR
              || pTerm->iConState == CON_OBJECT_EDITOR
              || pTerm->iConState == CON_ROOM_EDITOR )
              && pTerm->pChar != NULL
              && pTerm->pChar->iLevel >= BUILDER_LEVEL
              && !IS_SET( pTerm->pChar->pPCData->fChanFlags,
              FLAG_CHANNEL_NO_REMOTE_TELL )
              && str_compare( pTerm->pChar->pPCData->sName, cBuf2 )
              == TRUE )
            {
                write_string_buffer( pTerm, cBuf );
                b = 1;
                break;
            }
        }
    }
    else
    {
        for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
        {
            if ( ( pTerm->iConState == CON_PLAYING
              || pTerm->iConState == CON_NPC_EDITOR
              || pTerm->iConState == CON_OBJECT_EDITOR
              || pTerm->iConState == CON_ROOM_EDITOR )
              && pTerm->pChar != NULL
              && !IS_SET( pTerm->pChar->pPCData->fChanFlags,
              FLAG_CHANNEL_NO_REMOTE_TELL )
              && str_compare( pTerm->pChar->pPCData->sName, cBuf2 )
              == TRUE )
            {
                write_string_buffer( pTerm, cBuf );
                b = 1;
                break;
            }
        }
    }

    if ( pTerm == NULL )
        b         = 0;

    b2            = MUD_COMM_CMD_REPLY_TELL;
    write_mudcomm_data_buffer( pMUDComm, &b2, 1 );
    write_mudcomm_data_buffer( pMUDComm, &b, 1 );
}


void mudcomm_cmd_mail( MUD_COMM_DATA *pMUDComm )
{
    /*
     * Perposed standard for cross-MUD mail command:
     *
     * <null-terminated string>         - destinaton player name
     * <null-terminated string>         - subject
     * <2 byte integer>                 - sender's privileges (level)
     * <null-terminated string>         - sender
     * <null-terminated string>         - message body
     */
}


void mudcomm_cmd_who( MUD_COMM_DATA *pMUDComm )
{
    TERM_DATA *pTerm;
    CHAR_DATA *pChar;
    char cBuf[256];
    char b = MUD_COMM_CMD_REPLY_WHO;

    write_mudcomm_data_buffer( pMUDComm, &b, 1 );

    for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
    {
        if ( ( pChar = pTerm->pChar ) != NULL && !IS_GUEST( pChar )
          && ( pTerm->iConState == CON_PLAYING
          || pTerm->iConState == CON_NPC_EDITOR
          || pTerm->iConState == CON_OBJECT_EDITOR
          || pTerm->iConState == CON_ROOM_EDITOR ) )
        {
            sprintf( cBuf, "%d", pChar->iLevel );
            write_mudcomm_data_buffer( pMUDComm, cBuf,
              ( strlen( cBuf ) + 1 ) );
            write_mudcomm_data_buffer( pMUDComm, pChar->pPCData->sName,
              ( strlen( pChar->pPCData->sName ) + 1 ) );
        }
    }
}


void mudcomm_cmd_users( MUD_COMM_DATA *pMUDComm )
{
    /*
     * Perposed standard for remote users command:
     *
     * <2 byte integer>                 - sender's privileges (level)
     *
     * <null-terminated string>         - hostname
     * <null-terminated string>         - other info
     * ...                              - the above two any number of times
     */
}


void mudcomm_cmd_reply_mail( MUD_COMM_DATA *pMUDComm )
{
    /*
     * Perposed standard for cross-MUD mail reply:
     *
     * <1 byte integer>                 - if > 0 successful, else failure
     */
}


void mudcomm_cmd_reply_tell( MUD_COMM_DATA *pMUDComm )
{
    GENERIC_DATA *pGen;
    TERM_DATA *pTerm;

    if ( pMUDComm->pWaitList == NULL )
        return;

    if ( ( pTerm = pMUDComm->pWaitList->pData ) == NULL )
        goto end;

    if ( pMUDComm->iInBufSize > 1 && pMUDComm->pInBuffer[1] > 0 )
        write_string_buffer( pTerm, "\n\rYour message got through.\n\r" );
    else
        write_string_buffer( pTerm,
          "\n\rYour message failed to get through.\n\r" );

end:
    pGen                = pMUDComm->pWaitList;
    pMUDComm->pWaitList = pGen->pNext;
    free_mem( (void **) &pGen );
}


/*
 * Deal with MUD_COMM_CMD_REPLY_WHO commands.
 */
void mudcomm_cmd_reply_who( MUD_COMM_DATA *pMUDComm )
{
    GENERIC_DATA *pGen;
    TERM_DATA *pTerm;
    char cBuf[MAX_STRING];
    char cBuf2[MAX_STRING];
    char cBuf3[MAX_STRING];
    int i;
    int iPos;

    if ( pMUDComm->pWaitList == NULL )
        return;

    if ( ( pTerm = pMUDComm->pWaitList->pData ) == NULL )
        goto end;

    if ( pMUDComm->iInBufSize <= 2 )
    {
        snprintf( cBuf, MAX_STRING,
          "\n\rThere are no players on %s at the moment.\n\r",
          pMUDComm->pMUDName );
        write_string_buffer( pTerm, cBuf );
        goto end;
    }

    setup_string_pager( pTerm );

    snprintf( cBuf, MAX_STRING, "\n\r_____ Players on %s _____\n\r\n\r",
      pMUDComm->pMUDName );
    page_string( pTerm, cBuf );

    for ( iPos = 1; ; )
    {
        for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
          && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
            cBuf2[i]    = pMUDComm->pInBuffer[iPos++];

        cBuf2[i]        = '\0';

        if ( ++iPos >= pMUDComm->iInBufSize )
            break;

        for ( i = 0; iPos < pMUDComm->iInBufSize && i < ( MAX_STRING - 1 )
          && pMUDComm->pInBuffer[iPos] != '\0'; i++ )
            cBuf3[i]    = pMUDComm->pInBuffer[iPos++];

        cBuf3[i]        = '\0';

        snprintf( cBuf, MAX_STRING, "[%3.3s] %s\n\r", cBuf2, cBuf3 );
        page_string( pTerm, cBuf );

        if ( ++iPos >= pMUDComm->iInBufSize )
            break;
    }

    finish_string_pager( pTerm );

end:
    pGen                = pMUDComm->pWaitList;
    pMUDComm->pWaitList = pGen->pNext;
    free_mem( (void **) &pGen );
}


void mudcomm_cmd_reply_users( MUD_COMM_DATA *pMUDComm )
{
    /*
     * Perposed standard for remote users reply:
     *
     * [Refer to the note in mudcomm_cmd_users()]
     */
}


/*
 * End of mud_comm.c
 */