gnuworld/src/server_connection.cc

458 lines
9.1 KiB
C++

/**
* server_connection.cc
* This is the implementation file for the xServer class.
* This class is the entity which is the GNUWorld server
* proper. It manages network I/O, parsing and distributing
* incoming messages, notifying attached clients of
* system events, on, and on, and on.
*
* Copyright (C) 2002 Daniel Karrels <dan@karrels.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
* $Id: server_connection.cc,v 1.4 2006/12/22 06:41:45 kewlio Exp $
*/
#include <new>
#include <string>
//#include <list>
//#include <vector>
//#include <algorithm>
#include <sstream>
//#include <fstream>
//#include <stack>
//#include <iostream>
//#include <utility>
//#include <cstdlib>
#include <cstdio>
#include <cstdarg>
//#include <cstring>
//#include <cassert>
//#include <cerrno>
//#include <csignal>
#include "gnuworld_config.h"
//#include "misc.h"
//#include "events.h"
//#include "ip.h"
#include "server.h"
#include "Network.h"
//#include "iServer.h"
//#include "iClient.h"
//#include "EConfig.h"
//#include "match.h"
#include "ELog.h"
//#include "StringTokenizer.h"
//#include "xparameters.h"
//#include "moduleLoader.h"
//#include "ServerTimerHandlers.h"
//#include "LoadClientTimerHandler.h"
//#include "UnloadClientTimerHandler.h"
#include "ConnectionManager.h"
#include "ConnectionHandler.h"
#include "Connection.h"
RCSTAG( "$Id: server_connection.cc,v 1.4 2006/12/22 06:41:45 kewlio Exp $" ) ;
namespace gnuworld
{
//using std::pair ;
//using std::make_pair ;
using std::string ;
//using std::vector ;
//using std::list ;
using std::clog ;
using std::endl ;
using std::stringstream ;
//using std::stack ;
//using std::unary_function ;
void xServer::OnConnect( Connection* theConn )
{
// Just connected to our uplink
serverConnection = theConn ;
// P10 version information, bogus.
Version = 10 ;
// Set ourselves as a service.
me->setService() ;
// Initialize the connection time variable to current time.
ConnectionTime = ::time( NULL ) ;
clog << "*** Connected!"
<< endl ;
elog << "*** Connected to "
<< serverConnection->getHostname()
<< ", port "
<< serverConnection->getRemotePort()
<< endl ;
// Login to the uplink.
WriteDuringBurst( "PASS :%s\n", Password.c_str() ) ;
// Send our server information.
WriteDuringBurst( "SERVER %s %d %d %d J%02d %s +s :%s\n",
ServerName.c_str(),
1,
StartTime,
ConnectionTime,
Version,
(string( getCharYY() ) + "]]]").c_str(),
ServerDescription.c_str() ) ;
}
void xServer::OnConnectFail( Connection* theConn )
{
elog << "xServer::OnConnectFail> Failed to establish connection "
<< "to "
<< theConn->getHostname()
<< ":"
<< theConn->getRemotePort()
<< endl ;
serverConnection = 0 ;
keepRunning = false ;
}
/**
* Handle a disconnect from our uplink. This method is
* responsible for deallocating variables mostly.
*/
void xServer::OnDisconnect( Connection* theConn )
{
if( theConn != serverConnection )
{
elog << "xServer::OnDisconnect> Unknown connection"
<< endl ;
return ;
}
// Disconnected from uplink
// The ConnectionManager will deallocate the memory associated with
// the Connection object
serverConnection = 0 ;
elog << "xServer::OnDisconnect> Disconnected :("
<< endl ;
keepRunning = false ;
// doShutdown() will be called at the bottom of the main for loop,
// which will perform a proper shutdown.
for( xNetwork::localClientIterator cItr = Network->localClient_begin() ;
cItr != Network->localClient_end() ; ++cItr )
{
cItr->second->OnDisconnect() ;
}
}
void xServer::OnRead( Connection* theConn, const string& line )
{
if( theConn != serverConnection )
{
elog << "xServer::OnRead> Unknown connection"
<< endl ;
return ;
}
// Don't process any incoming data on the last iteration of
// the main control loop.
if( !keepRunning || lastLoop )
{
// Part of the shutdown process includes flushing any
// data in the output buffer, and closing connections.
// This requires calling Poll(), which will also
// recv() and perform any distribution of messages to
// handlers, including OnRead().
// Therefore, only handle data if the server is still
// in a running state.
return ;
}
burstLines++ ;
burstBytes += line.size() ;
size_t len = line.size() - 1 ;
while( ('\n' == line[ len ]) || ('\r' == line[ len ]) )
{
--len ;
}
memset( inputCharBuffer, 0, sizeof( inputCharBuffer ) ) ;
strncpy( inputCharBuffer, line.c_str(), len + 1 ) ;
if( verbose )
{
clog << "[IN ]: "
<< line ;
}
if( logSocket )
{
socketFile << line ;
}
Process( inputCharBuffer ) ;
// Post the RAW read event
PostEvent( EVT_RAW, static_cast< void* >(
const_cast< string* >( &line ) ) ) ;
}
/**
* This method will append a string to the output
* buffer.
* Returns false if there is no valid connection,
* true otherwise.
*/
bool xServer::Write( const string& buf )
{
// Is there a valid connection?
if( !isConnected() )
{
return false ;
}
if( verbose )
{
// Output the debugging information
// to the console.
clog << "[OUT]: " << buf ;
// Should we output a trailing newline
// character?
if( buf[ buf.size() - 1 ] != '\n' )
{
clog << endl ;
}
}
// Newline terminate the string if it's
// not already done and append it to
// the output buffer.
//
if( buf[ buf.size() - 1 ] != '\n' )
{
if( useHoldBuffer )
{
burstHoldBuffer += buf + '\n' ;
}
else
{
serverConnection->Write( buf + '\n' ) ;
}
}
else
{
if( useHoldBuffer )
{
burstHoldBuffer += buf ;
}
else
{
serverConnection->Write( buf ) ;
}
}
// Return success.
return true ;
}
bool xServer::WriteDuringBurst( const string& buf )
{
// Is there a valid connection?
if( !isConnected() )
{
elog << "xServer::WriteDuringBurst> Not connected"
<< endl ;
return 0 ;
}
if( verbose )
{
// Output the debugging information
// to the console.
clog << "[OUT]: " << buf ;
// Should we output a trailing newline
// character?
if( buf[ buf.size() - 1 ] != '\n' )
{
clog << endl ;
}
}
// Newline terminate the string if it's
// not already done and append it to
// the output buffer.
//
serverConnection->Write( buf ) ;
if( buf[ buf.size() - 1 ] != '\n' )
{
serverConnection->Write( string( "\n" ) ) ;
}
return true ;
}
/**
* Write the contents of a std::stringstream to the uplink connection.
*/
bool xServer::Write( const stringstream& s )
{
return Write( string( s.str() ) ) ;
}
bool xServer::WriteDuringBurst( const stringstream& s )
{
return WriteDuringBurst( string( s.str() ) ) ;
}
/**
* This method appends the variable sized argument
* list buffer to the output buffer.
* Returns false if no valid connection available,
* true otherwise.
* I despise this function. --dan
*/
bool xServer::Write( const char* format, ... )
{
// Is there a valid connection?
if( !isConnected() )
{
// Nope, return false.
return false ;
}
// Go through the motions of putting the
// string into a buffer.
char buffer[ 4096 ] = { 0 } ;
va_list _list ;
va_start( _list, format ) ;
vsnprintf( buffer, 4096, format, _list ) ;
va_end( _list ) ;
if( verbose )
{
// Output the string to the console.
clog << "[OUT]: " << buffer ;
// Do we need to newline terminate it?
if( buffer[ strlen( buffer ) - 1 ] != '\n' )
{
clog << endl ;
}
}
if( buffer[ strlen( buffer ) - 1 ] != '\n' )
{
if( useHoldBuffer )
{
burstHoldBuffer += buffer ;
burstHoldBuffer += "\n" ;
}
else
{
serverConnection->Write( buffer ) ;
serverConnection->Write( string( "\n" ) ) ;
}
}
else
{
if( useHoldBuffer )
{
burstHoldBuffer += buffer ;
}
else
{
serverConnection->Write( buffer ) ;
}
}
return true ;
}
bool xServer::WriteDuringBurst( const char* format, ... )
{
// Is there a valid connection?
if( !isConnected() )
{
// Nope, return false.
return false ;
}
// Go through the motions of putting the
// string into a buffer.
char buffer[ 4096 ] = { 0 } ;
va_list _list ;
va_start( _list, format ) ;
vsnprintf( buffer, 4096, format, _list ) ;
va_end( _list ) ;
if( verbose )
{
// Output the string to the console.
clog << "[OUT]: " << buffer ;
// Do we need to newline terminate it?
if( buffer[ strlen( buffer ) - 1 ] != '\n' )
{
clog << endl ;
}
}
// Append the line to the output buffer.
serverConnection->Write( buffer ) ;
if( buffer[ strlen( buffer ) - 1 ] != '\n' )
{
serverConnection->Write( string( "\n" ) ) ;
}
// Return success
return true ;
}
void xServer::WriteBurstBuffer()
{
if( !isConnected() )
{
return ;
}
serverConnection->Write( burstHoldBuffer.data() ) ;
burstHoldBuffer.clear() ;
}
void xServer::FlushData()
{
if( !isConnected() )
{
return ;
}
serverConnection->Flush() ;
}
} // namespace gnuworld