roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * Mdsp.h
 *   Stuff to deal with MDSP
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */

// C Includes
#include <arpa/telnet.h>

// Mud Includes
#include "mud.h"
#include "msdp.h"
#include "login.h"

#define MSDP_DEBUG

void Server::processMsdp(void) {
	for(Socket *sock : sockets) {
		if(sock->getState() == CON_DISCONNECTING)
			continue;
		if(sock->getMccp() || sock->getAtcp()) {
			for(std::pair<bstring, ReportedMsdpVariable*> p : sock->msdpReporting) {
				ReportedMsdpVariable* var = p.second;
				if(var->getRequiresPlayer() && (!sock->getPlayer() || sock->getState() != CON_PLAYING)) continue;
				if(!var->checkTimer()) continue;
				var->update();
				if(!var->isDirty()) continue;
				sock->msdpSend(var);
				var->setDirty(false);
			}
		}
	}
}

bool Socket::processMsdpVarVal(bstring& variable, bstring& value) {
#ifdef MSDP_DEBUG
	std::cout << "Found Var: '" << variable << "' Val: '" << value << "'"
			<< std::endl;
#endif
	if (variable.equals("LIST")) {
		return (msdpList(value));
	} else if (variable.equals("REPORT")) {
		return (msdpReport(value));
	} else if (variable.equals("UNREPORT")) {
		return (msdpUnReport(value));
	} else if (variable.equals("SEND")) {
		return (msdpSend(value));
	} else {
		// See if they've sent us a configurable variable, if so set it
		for(std::pair<bstring, MsdpVariable*> p : gConfig->msdpVariables ) {
			MsdpVariable* msdpVar = p.second;
			if(msdpVar->isConfigurable()) {
				if(!isReporting(variable)) {
					// If we're not reporting, start reporting it
					msdpReport(variable);
				}
				ReportedMsdpVariable* reportedVar = getReportedMsdpVariable(variable);

				// Should never happen since we just added it
				if(!reportedVar)
					return(false);
				// If it's a write once variable, we can only set it if the value is currently unknown
				if(msdpVar->isWriteOnce() && reportedVar->getValue() != "unknown")
					return(false);
				reportedVar->setValue(value);
#ifdef MSDP_DEBUG
				std::cout << "processMsdpVarVal: Set configurable variable '" << variable << "' to '" << value << "'" << std::endl;
#endif
				return(true);
			}
		}
#ifdef MSDP_DEBUG
		std::cout << "processMsdpVarVal: Unknown variable '" << variable << "'"
				<< std::endl;
#endif
	}
	return (true);
}
bool Socket::msdpList(bstring& value) {
	if (value.equals("COMMANDS")) {
		const char MsdpCommandList[] = "LIST REPORT RESET SEND UNREPORT";
		msdpSendList(value, MsdpCommandList);
		return (true);
	} else if (value.equals("LISTS")) {
		const char MsdpLists[] =
				"COMMANDS LISTS CONFIGURABLE_VARIABLES REPORTABLE_VARIABLES REPORTED_VARIABLES SENDABLE_VARIABLES";
		msdpSendList(value, MsdpLists);
		return (true);
	} else if (value.equals("SENDABLE_VARIABLES")
			|| value.equals("REPORTABLE_VARIABLES")) {
		// Same list for now
		std::ostringstream oStr;
		for (std::pair<bstring, MsdpVariable*> p : gConfig->msdpVariables) {
			oStr << " " << p.second->getName();
		}
		msdpSendList(value, bstring(oStr.str()).trim());
		return (true);
	} else if (value.equals("CONFIGURABLE_VARIABLES")) {
		std::ostringstream oStr;
		for (std::pair<bstring, MsdpVariable*> p : gConfig->msdpVariables) {
			if (p.second->isConfigurable())
				oStr << " " << p.second->getName();
		}
		msdpSendList(value, bstring(oStr.str()).trim());
		return (true);
	} else if (value.equals("REPORTED_VARIABLES")) {
		std::ostringstream oStr;
		for (std::pair<bstring, ReportedMsdpVariable*> p : msdpReporting) {
			oStr << " " << p.second->getName();
		}
		msdpSendList("REPORTED_VARIABLES", bstring(oStr.str()).trim());
		return (true);
	} else if (value.equals("")) {
		// If we just get a LIST command, send off a list of all variables
		std::ostringstream oStr;
		for (std::pair<bstring, MsdpVariable*> p : gConfig->msdpVariables) {
			oStr << " " << p.second->getName();
		}
		msdpSendList("REPORTABLE_VARIABLES", bstring(oStr.str()).trim());
		return (true);
	}

	return (false);
}
ReportedMsdpVariable* Socket::getReportedMsdpVariable(bstring& value) {
	std::map<bstring, ReportedMsdpVariable*>::iterator it = msdpReporting.find(value);

	if (it == msdpReporting.end())
		return (NULL);
	else
		return (it->second);
}
bool Socket::isReporting(bstring& value) {
	std::map<bstring, ReportedMsdpVariable*>::iterator it = msdpReporting.find(value);

	if (it == msdpReporting.end())
		return (false);
	else
		return (true);
}

bool Socket::msdpReport(bstring& value) {
	MsdpVariable* msdpVar = gConfig->getMsdpVariable(value);
	if (!msdpVar || msdpVar->getName() != value)
		return (false);

	if (isReporting(value)) {
#ifdef MSDP_DEBUG
		std::cout << "MsdpHandleReport: Already Reporting '" << value << "'"
				<< std::endl;
#endif
		return (true);
	}

	msdpReporting[msdpVar->getName()] = new ReportedMsdpVariable(msdpVar, this);
#ifdef MSDP_DEBUG
	std::cout << "MsdpHandleReport: Now Reporting '" << msdpVar->getName()
			<< "'" << std::endl;
#endif
	return (true);
}
bool Socket::msdpReset(bstring& value) {
	if (value.equals("REPORTABLE_VARIABLES")
			|| value.equals("SENDABLE_VARIABLES")) {
		// For now just clear all reported variables
		msdpClearReporting();
	}
	return (false);
}
void Socket::msdpClearReporting() {
	for (std::pair<bstring, ReportedMsdpVariable*> p : msdpReporting) {
		delete (p.second);
	}
	msdpReporting.clear();
}
bool Socket::msdpSend(bstring value) {
	MsdpVariable *msdpVar = gConfig->getMsdpVariable(value);
	if(msdpVar == NULL) {
#ifdef MSDP_DEBUG
		std::cout << "Unknown variable to send: '" << value << "'" << std::endl;
#endif
		return (false);
	}
	if(msdpVar->hasSendScript()) {
		gServer->runPython(msdpVar->getSendScript(), "", this, getPlayer(), msdpVar);
		return(true);
	} else {
		return(msdpSend(getReportedMsdpVariable(value)));
	}
}
bool Socket::msdpSend(ReportedMsdpVariable* reportedVar) {
	if(reportedVar == NULL)
		return(false);

	if(reportedVar->getRequiresPlayer() == true && getPlayer() == NULL)
		return(false);

	// Send scripts will trump locally set value
	if(reportedVar->hasSendScript())
		gServer->runPython(reportedVar->getSendScript(), "", this, getPlayer(), reportedVar);
	else {
		if(reportedVar == NULL)
			msdpSendPair(reportedVar->getName(), "N/A");
		else
			msdpSendPair(reportedVar->getName(), reportedVar->getValue());
	}
	return(true);

}
bool Socket::msdpUnReport(bstring& value) {
	std::map<bstring, ReportedMsdpVariable*>::iterator it = msdpReporting.find(value);

	if (it == msdpReporting.end())
		return (false);
	else {
#ifdef MSDP_DEBUG
		std::cout << "MsdpHandleUnReport: No longer reporting '" << value << "'" << std::endl;
#endif
		delete it->second;
		msdpReporting.erase(it);
		return (true);
	}
	return (false);
}
void Socket::msdpSendList(bstring variable, bstring value) {
	std::ostringstream oStr;

	if (getMsdp()) {
		value.Replace(' ', (unsigned char) MSDP_VAL);
		oStr
				<< (unsigned char) IAC << (unsigned char) SB << (unsigned char) TELOPT_MSDP
				<< (unsigned char) MSDP_VAR << variable << (unsigned char) MSDP_VAL
				<< (unsigned char) MSDP_ARRAY_OPEN << (unsigned char) MSDP_VAL << value
				<< (unsigned char) MSDP_ARRAY_CLOSE << (unsigned char) IAC << (unsigned char) SE;
	} else if (getAtcp()) {
		oStr
				<< (unsigned char) IAC << (unsigned char) SB << (unsigned char) TELOPT_ATCP
				<< "MSDP." << variable << " " << value
				<< (unsigned char) IAC << (unsigned char) SE;
	}

	bprint(oStr.str());

}
void Socket::msdpSendPair(bstring variable, bstring value) {
	if (variable.empty() || value.empty())
		return;

	std::ostringstream oStr;

	if (this->getMsdp()) {
		oStr
				<< (unsigned char) IAC << (unsigned char) SB << (unsigned char) TELOPT_MSDP
				<< (unsigned char) MSDP_VAR << variable
				<< (unsigned char) MSDP_VAL << value
				<< (unsigned char) IAC << (unsigned char) SE;
	} else if (getAtcp()) {
		oStr
				<< (unsigned char) IAC << (unsigned char) SB << (unsigned char) TELOPT_ATCP
				<< "MSDP." << variable << " " << value
				<< (unsigned char) IAC << (unsigned char) SE;
	}

	bprint(oStr.str());
}



MsdpVariable* Config::getMsdpVariable(bstring& name) {
	std::map<bstring, MsdpVariable*>::iterator it = msdpVariables.find(name);
	if(it == msdpVariables.end())
		return(NULL);
	else
		return(it->second);
}
void MsdpVariable::init() {
	configurable = writeOnce = false;
	requiresPlayer = true; // Defaults to true, manually disable via xml
	updateInterval = 1;
	name ="unknown";
}
MsdpVariable::MsdpVariable(xmlNodePtr rootNode) {
	init();
	xmlNodePtr curNode = rootNode->children;

		while(curNode) {
				 if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode);
			else if(NODE_NAME(curNode, "SendScript")) xml::copyToBString(sendScript, curNode);
			else if(NODE_NAME(curNode, "UpdateScript")) xml::copyToBString(updateScript, curNode);
			else if(NODE_NAME(curNode, "RequiresPlayer")) xml::copyToBool(requiresPlayer, curNode);
			else if(NODE_NAME(curNode, "Configurable")) xml::copyToBool(configurable, curNode);
			else if(NODE_NAME(curNode, "WriteOnce")) xml::copyToBool(writeOnce, curNode);
			else if(NODE_NAME(curNode, "UpdateInterval")) xml::copyToNum(updateInterval, curNode);
			curNode = curNode->next;
		}

	if(name.empty())
		throw(std::runtime_error("No Name for MSDP Variable!\n"));
//	else
//		std::cout << "New MSDP Variable '" << name << "'" << std::endl;
}
MsdpVariable::MsdpVariable() {
	init();
}

ReportedMsdpVariable::ReportedMsdpVariable(const MsdpVariable* mv, Socket* sock) {
	name = mv->getName();
	parentSock = sock;
	configurable = mv->isConfigurable();
	writeOnce = mv->isWriteOnce();
	sendScript = mv->getSendScript();
	updateScript = mv->getUpdateScript();
	updateInterval = mv->getUpdateInterval();
	timer.setDelay(updateInterval);

	dirty = true;
	value = "unknown";
}

bstring MsdpVariable::getName() const {
	return(name);
}

bstring MsdpVariable::getSendScript() const {
	return(sendScript);
}

bstring MsdpVariable::getUpdateScript() const {
	return(updateScript);
}

bool MsdpVariable::isConfigurable() const {
	return(configurable);
}

bool MsdpVariable::isWriteOnce() const {
	return(writeOnce);
}

bool MsdpVariable::getRequiresPlayer() const {
	return(requiresPlayer);
}
int MsdpVariable::getUpdateInterval() const {
	return(updateInterval);
}
bool MsdpVariable::hasSendScript() const {
	return(!sendScript.empty());
}
bool MsdpVariable::hasUpdateScript() const {
	return(!updateScript.empty());
}
bstring ReportedMsdpVariable::getValue() const {
	return(value);
}
bool ReportedMsdpVariable::checkTimer() {
	if(timer.hasExpired()) {
		timer.update(getUpdateInterval());
		return(true);
	} else {
		return(false);
	}
}

void ReportedMsdpVariable::setValue(bstring newValue) {
	if(value != newValue) {
		value = newValue;
		dirty = true;
	}
}
void ReportedMsdpVariable::setValue(int newValue) {
	return(setValue(bstring(newValue)));
}
void ReportedMsdpVariable::setValue(long newValue) {
	return(setValue(bstring(newValue)));
}
bool ReportedMsdpVariable::isDirty() const {
	return(dirty);
}

void ReportedMsdpVariable::update() {
	if(!hasUpdateScript())
		return;
	if(!parentSock)
		return;

	gServer->runPython(getUpdateScript(), "", parentSock, parentSock->getPlayer(), this);
}

void ReportedMsdpVariable::setDirty(bool pDirty) {
	dirty = pDirty;
}


//*********************************************************************
//						loadMsdpVariables
//*********************************************************************

bool Config::loadMsdpVariables() {
	xmlDocPtr	xmlDoc;

	xmlNodePtr	curNode;
	char		filename[80];

	// build an XML tree from a the file
	sprintf(filename, "%s/msdp.xml", Path::Code);

	xmlDoc = xml::loadFile(filename, "MsdpVariables");
	if(xmlDoc == NULL)
		return(false);

	curNode = xmlDocGetRootElement(xmlDoc);

	curNode = curNode->children;
	while(curNode && xmlIsBlankNode(curNode)) {
		curNode = curNode->next;
	}
	if(curNode == 0) {
		xmlFreeDoc(xmlDoc);
		xmlCleanupParser();
		return(false);
	}

	clearMsdpVariables();
	while(curNode != NULL) {
		if(NODE_NAME(curNode, "MsdpVariable")) {
			MsdpVariable* msdpVar = new MsdpVariable(curNode);
			if(msdpVar)
				msdpVariables[msdpVar->getName()] = msdpVar;
		}
		curNode = curNode->next;
	}
	xmlFreeDoc(xmlDoc);
	xmlCleanupParser();
	return(true);
}

void Config::clearMsdpVariables() {
	for(std::pair<bstring,MsdpVariable*> p : msdpVariables) {
		delete p.second;
	}
	msdpVariables.empty();
	for(Socket* sock : gServer->sockets ) {
		if(sock->getAtcp() || sock->getMsdp())
			sock->msdpClearReporting();
	}
}