/*
 * Copyright (C) 2005-2008 MaNGOS 
 *
 * Copyright (C) 2008 Trinity 
 *
 * 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 Trinityd
*/
#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.
SOCKET r;
#define dropclient {Sendf("I'm busy right now, come back later."); \
        SetCloseAndDelete(); \
        return; \
    }
uint32 iSession=0;                                          ///< Session number (incremented each time a new connection is made)
unsigned int iUsers=0;                                      ///< Number of active administrators
typedef int(* pPrintf)(const char*,...);
void ParseCommand(CliCommandHolder::Print*, 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=new char[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
void RASocket::OnAccept()
{
    std::string ss=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
void RASocket::OnRead()
{
    ///- Read data and check input length
    TcpSocket::OnRead();
    unsigned int sz=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 = new char [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
    bool gotenter=false;
    unsigned int y=0;
    for(;y  If the input is 'USER '
            case NONE:
                if(!memcmp(buff,"USER ",5))                 //got "USER" cmd
                {
                    szLogin=&buff[5];
                    ///- Get the gmlevel and password from the account table
                    std::string login = 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() If the input is 'PASS ' (and the user already gave his username)
            case LG:
                if(!memcmp(buff,"PASS ",5))                 //got "PASS" cmd
                {                                           //login+pass ok
                    ///- If password is correct, increment the number of active administrators
                    std::string login = szLogin;
                    std::string pw = &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)
                    {
                        delete check;
                        r=GetSocket();
                        stage=OK;
                        ++iUsers;
                        Sendf("+Logged in.\r\n");
                        sLog.outRALog("User %s has logged in.\n",szLogin.c_str());
                        Sendf("TC>");
                    }
                    else
                    {
                        ///- Else deny access
                        Sendf("-Wrong pass.\r\n");
                        sLog.outRALog("User %s has failed to log in.\n",szLogin.c_str());
                        if(bSecure)SetCloseAndDelete();
                    }
                }
                break;
                /// If user is logged, parse and execute the command
            case OK:
                if(strlen(buff))
                {
                    sLog.outRALog("Got '%s' cmd.\n",buff);
                    sWorld.QueueCliCommand(&RASocket::zprint , buff);
                }
                else
                    Sendf("TC>");
                break;
                ///
        };
    }
}
/// Output function
void RASocket::zprint( const char * szText )
{
    if( !szText )
        return;
    #ifdef RA_CRYPT
    char *megabuffer=strdup(szText);
    unsigned int sz=strlen(megabuffer);
    Encrypt(megabuffer,sz);
    send(r,megabuffer,sz,0);
    delete [] megabuffer;
    #else
    unsigned int sz=strlen(szText);
    send(r,szText,sz,0);
    #endif
}