/*
* 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();
}
}