/**
 * LOGINCommand.cc
 * Authenticates you with the bot
 *
 * 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: LOGINCommand.cc,v 1.32 2009/07/25 18:12:34 hidden1 Exp $
 */

#include	<string>
#include	<sstream>
#include	<iostream>

#include	<cstdlib>

#include	"Network.h"
#include	"ccontrol.h"
#include	"CControlCommands.h"
#include	"StringTokenizer.h"
#include	"md5hash.h" 
#include        "ccUser.h"
#include	"ip.h"
#include	"gnuworld_config.h"

RCSTAG( "$Id: LOGINCommand.cc,v 1.32 2009/07/25 18:12:34 hidden1 Exp $" ) ;

namespace gnuworld
{

using std::string ;
using std::endl ;
using std::ends ;
using std::stringstream ;

namespace uworld
{

bool LOGINCommand::Exec( iClient* theClient, const string& Message)
{
StringTokenizer st( Message ) ;

if( st.size() < 3 )
	{
	Usage(theClient);
	return true;
	}
//Try fetching the user authenticate entry
ccUser* theUser = bot->IsAuth(theClient);
if (theUser) 
	{
	//Dont let him authenticate under a new name (for now)
	bot->Notice(theClient, "You are already authenticated! See DEAUTH command.");
	return false;
	}

	/*
	 *  Find the user record, confirm authorisation and attach the record to this client. 
	 */
iServer* targetServer = Network->findServer( theClient->getIntYY() ) ;
if( NULL == targetServer )
	{
	elog	<< "LOGINCommand> Unable to find server: "
		<< theClient->getIntYY() << endl ;
	return false ;
	}
 
theUser = bot->GetOper(st[1]);
if (!theUser) 
	{

	bot->MsgChanLog("[FAILED LOGIN] %s - Bad Username: %s (%s)\n",
		theClient->getRealNickUserHost().c_str(),
		st[1].c_str(),
		targetServer->getName().c_str());
	if(theClient->isOper())
		bot->Notice(theClient, "FALSE LOGIN, DENIED");
	bot->addLogin(theClient);
	return false;
	}
else
	{ 
	//Check if the user need to be operd to login
	if((!theClient->isOper()) && (theUser->getNeedOp()))

		{
		bot->MsgChanLog("[FAILED LOGIN] %s - Not Oper'd\n",theClient->getRealNickUserHost().c_str());
		bot->addLogin(theClient);
		return false;
		}
	//Check if the users mask is in his access list
	if((!bot->UserGotMask(theUser,theClient->getRealNickUserHost()))
	    &&(!bot->UserGotMask( theUser,theClient->getNickName() + "!" + theClient->getUserName() + "@" + xIP(theClient->getIP()).GetNumericIP())))	
		{
		bot->MsgChanLog("[FAILED LOGIN] %s - No HostMask\n",theClient->getRealNickUserHost().c_str());
		if(theClient->isOper()) 
			bot->Notice(theClient, "FALSE LOGIN, DENIED");
		bot->addLogin(theClient);
		return false;
		}

	md5	hash; // MD5 hash algorithm object.
	md5Digest digest; // MD5Digest algorithm object.
	stringstream output;
	string salt = theUser->getPassword().substr(0, 8);
	string md5Part = theUser->getPassword().substr(8);
	string guess = salt + st.assemble(2);

	// Build a MD5 hash based on our salt + the guessed password.
	hash.update( (unsigned char *)guess.c_str(), strlen( guess.c_str() ));
	hash.report( digest );

	// Convert the digest into an array of int's to output as hex for 
	// comparison with the passwords generated by PHP.
	int data[ MD5_DIGEST_LENGTH ];
	int ii;
	for( ii = 0; ii < MD5_DIGEST_LENGTH; ii++ )
		{
		data[ii] = digest[ii];
		}
	output << hex;
	output.fill('0');
	for( ii = 0; ii < MD5_DIGEST_LENGTH; ii++ ) {
		output << setw(2) << data[ii];
		}
	output << ends;

	if (md5Part != output.str().c_str()) // If the MD5 hash's don't match..
		{
		bot->MsgChanLog("[FAILED LOGIN] %s - Bad Password (%s)\n",theClient->getRealNickUserHost().c_str(), targetServer->getName().c_str());
		if(theClient->isOper()) 
			bot->Notice(theClient, "FALSE LOGIN, DENIED");
		bot->addLogin(theClient);
		return false;
		}
	//Ok the password match , prepare the ccUser data
	if(theUser->getClient()) //there is already a user authenticated under that nick
		{
		const iClient *tClient = theUser->getClient();
		bot->Notice(tClient,"You have just been deauthenticated");
		bot->MsgChanLog("Login conflict for user %s from %s and %s (%s)\n",
				st[1].c_str(),theClient->getNickName().c_str(),
				tClient->getNickName().c_str(), targetServer->getName().c_str());
		bot->deAuthUser(theUser);
		}
	theUser->setUserName(st[1]);
	theUser->setNumeric(theClient->getCharYYXXX());
	//Try creating an authentication entry for the user
	if(bot->AuthUser(theUser,theClient))
		if(!(bot->isSuspended(theUser)))
			bot->Notice(theClient, "Authentication successful as %s! ",theUser->getUserName().c_str()); 
		else 
			bot->Notice(theClient, "Authentication successful as %s, "
				"however you are currently suspended ",
				theUser->getUserName().c_str()); 
	else if(theClient->isOper())
	        bot->Notice(theClient, "Error in authentication as %s",theUser->getUserName().c_str()); 
        bot->MsgChanLog("(%s) - %s: AUTHENTICATED (%s)\n",theUser->getUserName().c_str(),
                        theClient->getRealNickUserHost().c_str(), targetServer->getName().c_str());
	/* record their connection timestamp + numeric */
	theUser->setLastAuthTS(theClient->getConnectTime());
	theUser->setLastAuthNumeric(theClient->getCharYYXXX());
	} 

return true; 
} 

}
} // namespace gnuworld

