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