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