/////////////////////////////////////////////////////////// ///////////////// Have an itch? Scratch it! /////////////// ///////////////////////// SCRATCH ///////////////////////// ///////////////////// A MUD Server //////////////////// ///////////////////// By: Jared Devall //////////////////// ///////////////////// Thanks: //////////////////// ///////////////////// DIKU/Merc/ROM //////////////////// ///////////////////// Aetas/Deus Gang //////////////////// ///////////////////// Beej //////////////////// /////////////////////////////////////////////////////////// #include <cstdarg> #include <cstdio> #include <fstream> #include <map> #include <mysql/mysql.h> #include "smysql.h" #include "avatar.h" #include "colorTable.h" #include "stringutil.h" #include "timestamp.h" using namespace std; Avatar::Avatar( ) { _disconnected = false; _socket = new Socket; _gotInput = false; _status = LOGIN; } // get rid of the socket when avatar is deleted // disconnect and remove from list & delete Avatar::~Avatar( ) { if ( _socket ) { if ( !_socket->GetSocketIP().empty() ) { SocketServer::Instance()._socketList.remove( _socket ); SocketServer::Instance().KillSocket( _socket ); if ( Get( "name" ).empty() ) { cout << _socket->GetSocketIP() << " has disconnected @ " << Timestamp::Instance().GetDateTime() << "\n\r"; } else { cout << Get( "name" ) << " has disconnected @ " << Timestamp::Instance().GetDateTime() << "\n\r"; } } delete _socket; } } bool Avatar::Load( ) { string query; Mysql::LMAP::iterator avatar_info; query << "SELECT * FROM user WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'"; if ( !Mysql::Instance().Load( query.c_str() ) ) return false; avatar_info = Mysql::Instance()._info[0].find("username"); Set( "name", avatar_info->second ); avatar_info = Mysql::Instance()._info[0].find("password"); Set( "password", avatar_info->second ); avatar_info = Mysql::Instance()._info[0].find("_group"); Set( "group", avatar_info->second ); avatar_info = Mysql::Instance()._info[0].find("reply"); Set( "reply", avatar_info->second ); avatar_info = Mysql::Instance()._info[0].find("title"); Set( "title", avatar_info->second ); avatar_info = Mysql::Instance()._info[0].find("login"); Set( "login", avatar_info->second ); avatar_info = Mysql::Instance()._info[0].find("prompt"); Set( "prompt", avatar_info->second ); return true; } bool Avatar::Save( ) { string query; if ( Get( "group" ).empty() ) { Create(); return true; } // We have to escape certain strings because they may possibly have a single quote in them. query << "UPDATE user SET username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "', password='" << Mysql::Instance().EscapeString( Get( "password" ) ) << "', _group='" << Get( "group" ) << "',"; query << " login='" << Mysql::Instance().EscapeString( Get( "login" ) ) << "', reply='" << Mysql::Instance().EscapeString( Get( "reply" ) ) << "', title='" << Mysql::Instance().EscapeString( Get( "title" ) ) << "',"; query << " prompt='" << Mysql::Instance().EscapeString( Get( "prompt" ) ) << "' WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'"; Mysql::Instance().RealQuery( query.c_str() ); return true; } bool Avatar::Create( ) { string query; // We have to escape the name because it might have a single quote in it.. ' query << "INSERT INTO user ( username, password ) VALUES ( '" << Mysql::Instance().EscapeString( Get( "name" ) ) << "', '" << Get( "password" ) << "' )"; Mysql::Instance().Query( query.c_str() ); return true; } bool Avatar::Delete( ) { string query; query << "DELETE FROM user WHERE username='" << Mysql::Instance().EscapeString( Get( "name" ) ) << "'"; Mysql::Instance().Query( query.c_str() ); return true; } STATUS Avatar::GetStatus( ) { return _status; } void Avatar::SetStatus( STATUS status ) { _status = status; } void Avatar::Send( char * message, ... ) { va_list args; char buf[MAX_BUFFER]; string buf2; va_start( args, message ); vsprintf( buf, message, args); buf2 = _socket->GetOutput(); buf2.append( buf ); _socket->SetOutput( buf2 ); va_end( args ); } void Avatar::Send( const string &message ) { string buf; buf.append( message ); _socket->SetOutput( buf ); } void Avatar::SetSocket( Socket * socket ) { _socket = socket; } Socket * Avatar::GetSocket( ) { return _socket; } bool Avatar::SetName( const string &name ) { string buf; // Validate name if ( name.length() < 3 || name.length() > 15 ) { buf << name.length(); return false; } buf << name; for ( string::iterator cChar = buf.begin(); cChar != buf.end(); ++cChar ) { if ( !isalnum(*cChar) && (*cChar) != '\'' ) return false; } // Name is good Set( "name", name ); return true; } bool Avatar::VerifyPassword( const string &password ) { return Get( "password" ) == EncryptPassword( password ); } string Avatar::EncryptPassword( const string &password ) { return crypt( password.c_str(), "$1$zetyadr" ); } void Avatar::ClearPassword( ) { string blah = "e"; blah.erase(); Set( "password", blah ); } bool Avatar::Set( const string &set, const string &value ) { string buf; if ( !strcasecmp( set, "title" ) ) { if ( value[0] != ',' && value[0] != '\'' && value[0] != ' ' ) { buf << " " << value; } else { buf << value; } _info[set] = buf; } else if ( !strcasecmp( set, "name" ) ) { string buf; // Validate name if ( value.length() < 3 || value.length() > 15 ) { buf << value.length(); Send( "Names must be 3-15 characters in length.\n\r" ); return false; } buf << value; for ( string::iterator cChar = buf.begin(); cChar != buf.end(); ++cChar ) { if ( !isalnum(*cChar) && (*cChar) != '\'' ) { Send( "Names may only contain: alpha-numeric '\n\r" ); return false; } } _info[set] = buf; } else if ( !strcasecmp( set, "password" ) ) { _info[set] = value; } else { _info[set] = value; } return true; } string Avatar::Get( const string &get ) { map< string, string >::iterator info; return _info[get]; } void Avatar::SetEdit( const string &edit ) { if ( edit.empty() ) { _edit.erase(); } else { _edit = edit; } } string Avatar::GetEdit( ) { return _edit; } bool Avatar::HasPassword( ) { return Get( "password" ).empty(); } void Avatar::SetGotInput( bool value ) { _gotInput = value; } bool Avatar::GotInput( ) { return _gotInput; } void Avatar::FlushOutput( ) { string output; string buf2; string buf3; string colorClear; // This will add a prompt to the end of output // Ofcourse, one needs some text before it'll add a prompt // or we can add a prompt if we get some input and it calls for it. :) output = _socket->GetOutput(); if ( output.empty() != true || _gotInput ) { if ( output.find( string(_handlers.front()->Prompt( this ) )) != string::npos ) return; //if ( this->waiting == false ) { // Don't send a prompt if there's delayed text going if ( !_gotInput ) { output.insert( 0, "\n\r" ); } else { // Only if there's no input set gotInput to false // we do it like this so there isn't an extra newline // everytime you get some output if ( _socket->GetInput().empty() ) _gotInput = false; } output.append( "\n\r" + _handlers.front()->Prompt( (this) ) ); _socket->SetOutput( output ); //} } // This parses our color. colorClear << ColorTable::COLOR_ESCAPE << ColorTable::COLOR_CLEAR; buf3 = _socket->GetOutput(); buf3.append( colorClear ); for ( string::iterator cChar = buf3.begin(); cChar != buf3.end(); ++cChar ) { if ( (*cChar) == ColorTable::COLOR_ESCAPE ) { if ( ++cChar == buf3.end() ) break; buf2 << ColorTable::Substitute( (*cChar) ); } else { buf2 << (*cChar); // There was no color this char. } } _socket->SetOutput( buf2 ); } void Avatar::SetDisconnected( bool value ) { _disconnected = value; _socket->SetDisconnected( true ); } bool Avatar::IsDisconnected( ) { return _disconnected; } ////////////////////// HANDLERS ////////////////////////// void Avatar::ReplaceHandler( Handler * handler ) { PopHandler( ); StackHandler( handler ); } void Avatar::StackHandler( Handler * handler ) { _handlers.push_front( handler ); handler->Enter( this ); } void Avatar::ExitHandler( ) { PopHandler( ); _handlers.front()->Enter( this ) ; } void Avatar::PopHandler( ) { Handler * previous_handler = _handlers.front(); _handlers.pop_front( ); previous_handler->Exit( this ); } void Avatar::HandleInput( const std::string &input ) { string::iterator cChar; string string1; string string2; string2 = input; for ( cChar = string2.begin(); cChar != string2.end(); ++cChar ) { if ( ( (*cChar) == '\n' || (*cChar) == '\r' ) && cChar != string2.begin() ) { _handlers.front()->Handle( this, string1 ); string1.erase(); } else { string1 << (*cChar); } } _handlers.front()->Handle( this, string1 ); } ///////////////////// END HANDLERS //////////////////////