/**
 * PURGECommand.cc
 *
 * 24/01/2001 - Greg Sikorski <gte@atomicrevs.demon.co.uk>
 * Initial Version.
 *
 * Purges a channel.
 *
 * Caveats: None
 *
 * 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: PURGECommand.cc,v 1.22 2008/04/16 20:34:40 danielaustin Exp $
 */

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

#include	"StringTokenizer.h"
#include	"ELog.h"
#include	"cservice.h"
#include	"levels.h"
#include	"dbHandle.h"
#include	"Network.h"
#include	"responses.h"
#include	"cservice_config.h"

const char PURGECommand_cc_rcsId[] = "$Id: PURGECommand.cc,v 1.22 2008/04/16 20:34:40 danielaustin Exp $" ;

namespace gnuworld
{
using std::endl ;
using std::ends ;
using std::string ;
using std::stringstream ;

bool PURGECommand::Exec( iClient* theClient, const string& Message )
{
bot->incStat("COMMANDS.PURGE");

StringTokenizer st( Message ) ;
if( st.size() < 2 )
	{
	Usage(theClient);
	return true;
	}

/*
 *  Fetch the sqlUser record attached to this client. If there isn't one,
 *  they aren't logged in - tell them they should be.
 */

sqlUser* theUser = bot->isAuthed(theClient, true);
if (!theUser)
	{
	return false;
	}

/*
 *  Are we purging a user or a channel?
 */

// Did we find a '#' ?
if( string::npos == st[ 1 ].find_first_of( '#' ) )
{
	// Nope, look by user then.
	sqlUser* tmpUser = bot->getUserRecord(st[1]);

	if (!tmpUser)
		{
		bot->Notice(theClient,
			bot->getResponse(theUser,
				language::not_registered,
				string("The user %s doesn't appear to be registered.")).c_str(),
			st[1].c_str());
		return true;
		}

	int level = bot->getAdminAccessLevel(theUser);
	if (!theUser->getFlag(sqlUser::F_POWER))
	if (level < level::userpurge)
	{
		bot->Notice(theClient,
			bot->getResponse(theUser,
				language::insuf_access,
				string("You have insufficient access to perform that command")));
		return false;
	}

	int admLevel = bot->getAdminAccessLevel(tmpUser);
	/*
	 * Why I did these ??
	 *
	if (admLevel)
	{
		bot->Notice(theClient, "Cannot purge any user with '*' access. Try to remove it's '*' access first.");
		return false;
	}
	if (tmpUser->getFlag(sqlUser::F_NOPURGE))
	{
		bot->Notice(theClient, "User %s has NOPURGE flag. Remove it first.", tmpUser->getUserName().c_str());
		return false;
	}
	*/
	if (!theUser->getFlag(sqlUser::F_POWER))
	if ((level <= admLevel)/* && (tmpUser != theUser)*/) //<- reactivate this if admin can purge it's own user
	{
		bot->Notice(theClient, "Cannot purge a user with equal or higher access than your own");
		return false;
	}
	string userToDel = tmpUser->getUserName();
	if (bot->wipeUser(tmpUser->getID(), false))
	{
		bot->logAdminMessage("%s (%s) purged user %s", theClient->getNickName().c_str(), theUser->getUserName().c_str(), userToDel.c_str());
		bot->Notice(theClient, "Successfully purged user %s", userToDel.c_str());
	}
	else
	{
		bot->logDebugMessage("An error occured at a user purge command.");
		bot->Notice(theClient, "An error occured while purged user. Contact coders.");
		return false;
	}

	return true;
}	//End of user purge

if (st.size() < 3)
{
	Usage(theClient);
	return true;
}
/*
 *  First, check the channel isn't already registered.
 */

sqlChannel* theChan = bot->getChannelRecord(st[1]);
if ((!theChan) || (st[1] == "*"))
	{
	bot->Notice(theClient,
		bot->getResponse(theUser,
			language::chan_not_reg,
			string("%s isn't registered with me")).c_str(),
		st[1].c_str());
	return false;
	}

/*
 *  Check the user has sufficient access for this command..
 */
int level = bot->getAdminAccessLevel(theUser);
if (!theUser->getFlag(sqlUser::F_POWER))
if (level < level::chanpurge)
	{
	bot->Notice(theClient,
		bot->getResponse(theUser,
			language::insuf_access,
			string("You have insufficient access to perform that command")));
	return false;
	}

/*
 * Don't purge the channel if NOPURGE is set.
 */
if (!theUser->getFlag(sqlUser::F_POWER))
if (theChan->getFlag(sqlChannel::F_NOPURGE))
{
	bot->Notice(theClient, "%s has NOPURGE set, so I'm not purging it.",
		theChan->getName().c_str());
	return false;
}

bool reop;
string reason;

if (!strcasecmp(st[2],"-noop"))
{
	reason = st.assemble(3);
	reop = false;
} else {
	reason = st.assemble(2);
	reop = true;
}

/*
 * Fetch some information about the owner of this channel, so we can
 * 'freeze' it for future investigation in the log.
 */

stringstream managerQuery;
managerQuery	<< "SELECT users.user_name,users.email "
		<< "FROM users,levels "
		<< "WHERE levels.user_id = users.id "
		<< "AND levels.access = 500 "
		<< "AND levels.channel_id = "
		<< theChan->getID()
		<< " LIMIT 1"
		<< ends;

#ifdef LOG_SQL
	elog	<< "sqlQuery> "
		<< managerQuery.str().c_str()
		<< endl;
#endif

string manager = "No Manager";
string managerEmail = "No Email Address";

if( !bot->SQLDb->Exec(managerQuery, true ) )
//if( status != PGRES_TUPLES_OK )
	{
	elog	<< "PURGE> SQL Error: "
		<< bot->SQLDb->ErrorMessage()
		<< endl ;
	return false ;
	}
else
	{
		if (bot->SQLDb->Tuples() != 0)
		{
			manager = bot->SQLDb->GetValue(0,0);
			managerEmail = bot->SQLDb->GetValue(0,1);
		}
	}

/* If we need to reop, do it here */
if (reop)
{
	/* iterate over the channel userlist */
	vector< iClient* > opList;
	Channel* tmpChan = Network->findChannel(theChan->getName());
	/* only parse the following if the channel exists on the network
	 * if there is nobody in the channel, there is nobody to op.
	 */
	if (tmpChan)
	{
		for (Channel::userIterator chanUsers = tmpChan->userList_begin();
			chanUsers != tmpChan->userList_end(); ++chanUsers)
		{
			ChannelUser* tmpUser = chanUsers->second;
			iClient* tmpClient = tmpUser->getClient();
			sqlUser* tUser = bot->isAuthed(tmpClient, false);
			if (!tUser)
				continue;
			/* are they globally suspended? */
			if (tUser->getFlag(sqlUser::F_GLOBAL_SUSPEND))
				continue;
			sqlLevel* theLevel = bot->getLevelRecord(tUser, theChan);
			/* check if they have access (and not suspended) */
			if (theLevel)
			{
				if (theLevel->getSuspendExpire() > bot->currentTime())
					continue;
				if (theLevel->getAccess() >= 100)
				{
					/* they're 100+, op them */                        
					opList.push_back(tmpClient);
				}
			}
		}
		/* actually do the ops */
		if (!opList.empty())
		{
			/* check we are in the channel, and opped */
			ChannelUser* tmpBotUser = tmpChan->findUser(bot->getInstance());
			if (tmpBotUser)
			{
				if (!tmpBotUser->getMode(ChannelUser::MODE_O))
				{
					/* op ourselves so that we can do the reops */
					stringstream s;
					s	<< bot->getCharYY()
						<< " M "
						<< theChan->getName()
						<< " +o "
						<< bot->getCharYYXXX()
						<< ends;
					bot->Write( s );
					/* update the channel state */
					tmpBotUser->setMode(ChannelUser::MODE_O);
				}
			}
			/* do the ops - if we were not in the channel before, this will
			 * auto-join and op the bot anyway.
			 */
			bot->Op(tmpChan, opList);
		}
	}
}

/*
 * Reset everything back to nice default values.
 */

theChan->clearFlags();
theChan->setMassDeopPro(3);
theChan->setFloodPro(0);
theChan->setNoTake(0);
theChan->setURL("");
theChan->setDescription("");
theChan->setComment("");
theChan->setKeywords("");
theChan->setRegisteredTS(0);
theChan->setChannelMode("+tn");
theChan->commit();

/*
 * Permanently delete all associated Level records for this channel.
 */

stringstream theQuery ;

theQuery	<< "DELETE FROM levels WHERE channel_id = "
			<< theChan->getID()
			<< ends;

#ifdef LOG_SQL
elog	<< "sqlQuery> "
		<< theQuery.str().c_str()
		<< endl;
#endif

if( !bot->SQLDb->Exec(theQuery ) )
//if( status != PGRES_COMMAND_OK )
	{
	elog	<< "PURGE> SQL Error: "
		<< bot->SQLDb->ErrorMessage()
		<< endl ;
	return false ;
	}

/*
 * Bin 'em all.
 */

cservice::sqlLevelHashType::const_iterator ptr = bot->sqlLevelCache.begin();
cservice::sqlLevelHashType::key_type thePair;

while(ptr != bot->sqlLevelCache.end())
{
	sqlLevel* tmpLevel = ptr->second;
	unsigned int channel_id = ptr->first.second;

	if (channel_id == theChan->getID())
	{
		thePair = ptr->first;

#ifdef LOG_DEBUG
		elog << "Purging Level Record for: " << thePair.second << " (UID: " << thePair.first << ")" << endl;
#endif

		++ptr;
		bot->sqlLevelCache.erase(thePair);

		delete(tmpLevel);
	} else
	{
		++ptr;
	}

}

bot->logAdminMessage("%s (%s) has purged %s (%s)",
	theClient->getNickName().c_str(),
	theUser->getUserName().c_str(),
	theChan->getName().c_str(),
	reason.c_str());

bot->Notice(theClient,
	bot->getResponse(theUser, language::purged_chan,
		string("Purged channel %s")).c_str(),
	st[1].c_str());

bot->writeChannelLog(theChan,
	theClient,
	sqlChannel::EV_PURGE,
	"has purged " + theChan->getName() + " (" + reason + "), " +
	"Manager was " + manager + " (" + managerEmail + ")" );

/* Remove from cache.. part channel. */
bot->sqlChannelCache.erase(theChan->getName());
bot->sqlChannelIDCache.erase(theChan->getID());
/* no longer interested in this channel */
bot->getUplink()->UnRegisterChannelEvent( theChan->getName(), bot ) ;
/* remove mode 'R' (no longer registered) */
Channel* tmpChan = Network->findChannel(theChan->getName());
if (tmpChan)
	bot->getUplink()->Mode(NULL, tmpChan, string("-R"), string() );
bot->Part(theChan->getName());
bot->joinCount--;

delete(theChan);

return true ;
}

} // namespace gnuworld.
