* 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
*/
/** \file
\ingroup mangosd
*/
#include"Common.h"
#include"Database/DatabaseEnv.h"
#include"Log.h"
#include"RASocket.h"
#include"World.h"
#include"Config/ConfigEnv.h"
#include"Util.h"
#include"AccountMgr.h"
/// \todo Make this thread safe if in the future 2 admins should be able to log at the same time.
SOCKETr;
#define dropclient {Sendf("I'm busy right now, come back later."); \
SetCloseAndDelete(); \
return; \
}
uint32iSession=0;///< Session number (incremented each time a new connection is made)
unsignedintiUsers=0;///< Number of active administrators
typedefint(*pPrintf)(constchar*,...);
voidParseCommand(pPrintfzprintf,char*command);
/// RASocket constructor
RASocket::RASocket(ISocketHandler&h):TcpSocket(h)
{
///- Increment the session number
iSess=iSession++;
///- Get the config parameters
bSecure=sConfig.GetBoolDefault("RA.Secure",true);
iMinLevel=sConfig.GetIntDefault("RA.MinLevel",3);
///- Initialize buffer and data
iInputLength=0;
buff=newchar[RA_BUFF_SIZE];
stage=NONE;
}
/// RASocket destructor
RASocket::~RASocket()
{
///- Delete buffer and decrease active admins count
delete[]buff;
sLog.outRALog("Connection was closed.\n");
if(stage==OK)
iUsers--;
}
/// Accept an incoming connection
voidRASocket::OnAccept()
{
std::stringss=GetRemoteAddress();
sLog.outRALog("Incoming connection from %s.\n",ss.c_str());
///- If there is already an active admin, drop the connection
if(iUsers)
dropclient
///- Else print Motd
Sendf("%s\r\n",sWorld.GetMotd());
}
/// Read data from the network
voidRASocket::OnRead()
{
///- Read data and check input length
TcpSocket::OnRead();
unsignedintsz=ibuf.GetLength();
if(iInputLength+sz>=RA_BUFF_SIZE)
{
sLog.outRALog("Input buffer overflow, possible DOS attack.\n");
SetCloseAndDelete();
return;
}
///- If there is already an active admin (other than you), drop the connection
if(stage!=OK&&iUsers)
dropclient
char*inp=newchar[sz+1];
ibuf.Read(inp,sz);
/// \todo Can somebody explain this 'Linux bugfix'?
if(stage==NONE)
if(sz>4)//linux remote telnet
if(memcmp(inp,"USER ",5))
{
delete[]inp;return;
printf("lin bugfix");
}//linux bugfix
///- Discard data after line break or line feed
boolgotenter=false;
unsignedinty=0;
for(;y<sz;y++)
if(inp[y]=='\r'||inp[y]=='\n')
{
gotenter=true;
break;
}
//No buffer overflow (checked above)
memcpy(&buff[iInputLength],inp,y);
iInputLength+=y;
delete[]inp;
if(gotenter)
{
buff[iInputLength]=0;
iInputLength=0;
switch(stage)
{
/// <ul> <li> If the input is 'USER <username>'
caseNONE:
if(!memcmp(buff,"USER ",5))//got "USER" cmd
{
szLogin=&buff[5];
///- Get the gmlevel and password from the account table
std::stringlogin=szLogin;
///- Convert Account name to Upper Format
AccountMgr::normilizeString(login);
///- Escape the Login to allow quotes in names
loginDatabase.escape_string(login);
QueryResult*result=loginDatabase.PQuery("SELECT gmlevel FROM account WHERE username = '%s'",login.c_str());
///- If the user is not found, deny access
if(!result)
{
Sendf("-No such user.\r\n");
sLog.outRALog("User %s does not exist.\n",szLogin.c_str());
if(bSecure)SetCloseAndDelete();
}
else
{
Field*fields=result->Fetch();
//szPass=fields[0].GetString();
///- if gmlevel is too low, deny access
if(fields[0].GetUInt32()<iMinLevel)
{
Sendf("-Not enough privileges.\r\n");
sLog.outRALog("User %s has no privilege.\n",szLogin.c_str());
if(bSecure)SetCloseAndDelete();
}else
{
stage=LG;
}
deleteresult;
}
}
break;
///<li> If the input is 'PASS <password>' (and the user already gave his username)
caseLG:
if(!memcmp(buff,"PASS ",5))//got "PASS" cmd
{//login+pass ok
///- If password is correct, increment the number of active administrators
std::stringlogin=szLogin;
std::stringpw=&buff[5];
AccountMgr::normilizeString(login);
AccountMgr::normilizeString(pw);
loginDatabase.escape_string(login);
loginDatabase.escape_string(pw);
QueryResult*check=loginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))",login.c_str(),pw.c_str());
if(check)
{
deletecheck;
r=GetSocket();
stage=OK;
++iUsers;
Sendf("+Logged in.\r\n");
sLog.outRALog("User %s has logged in.\n",szLogin.c_str());