//
// This file is part of the aMule Project.
//
// Copyright (c) 2004-2009 Marcelo Roberto Jimenez ( phoenix@amule.org )
// Copyright (c) 2006-2009 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2009-2010 Retroshare Team
//
//
// 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.
// You are also granted to use it with the LGPL License
//
// 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 or the LGPL
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//

//This file uses libupnp

#define UPNP_C

#include "UPnPBase.h"
#include <stdio.h>
#include <string.h>
#include <sstream> // for std::istringstream

#include <algorithm>		// For transform()
#include "util/rsstring.h"

#ifdef __GNUC__
	#if __GNUC__ >= 4
		#define REINTERPRET_CAST(x) reinterpret_cast<x>
	#endif
#endif
#ifndef REINTERPRET_CAST
	// Let's hope that function pointers are equal in size to data pointers
	#define REINTERPRET_CAST(x) (x)
#endif


/**
 * Case insensitive std::string comparison
 */
bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
{
	std::string ns1(s1);
	std::string ns2(s2);
	std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower);
	std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower);
	return ns1 == ns2;
}


CUPnPPortMapping::CUPnPPortMapping(
	int ex_port,
	int in_port,
	const std::string &protocol,
	bool enabled,
	const std::string &description)
:
m_ex_port(),
m_in_port(),
m_protocol(protocol),
m_enabled(enabled ? "1" : "0"),
m_description(description),
m_key()
{
	rs_sprintf(m_ex_port, "%d", ex_port);
	rs_sprintf(m_in_port, "%d", in_port);
	m_key = m_protocol + m_ex_port;
}


const std::string &CUPnPLib::UPNP_ROOT_DEVICE =
	"upnp:rootdevice";

const std::string &CUPnPLib::UPNP_DEVICE_IGW =
	"urn:schemas-upnp-org:device:InternetGatewayDevice:1";
const std::string &CUPnPLib::UPNP_DEVICE_WAN =
	"urn:schemas-upnp-org:device:WANDevice:1";
const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION =
	"urn:schemas-upnp-org:device:WANConnectionDevice:1";
const std::string &CUPnPLib::UPNP_DEVICE_LAN =
	"urn:schemas-upnp-org:device:LANDevice:1";

const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING =
	"urn:schemas-upnp-org:service:Layer3Forwarding:1";
const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG =
	"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION =
	"urn:schemas-upnp-org:service:WANIPConnection:1";
const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION =
	"urn:schemas-upnp-org:service:WANPPPConnection:1";


CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint)
:
m_ctrlPoint(ctrlPoint)
{
}


std::string CUPnPLib::GetUPnPErrorMessage(int code) const
{
	return UpnpGetErrorMessage(code);
}


std::string CUPnPLib::processUPnPErrorMessage(
	const std::string &message,
	int errorCode,
	const DOMString errorString,
	IXML_Document *doc) const
{
	/* remove unused parameter warnings */
	(void) message;
	(void) errorCode;
	(void) errorString;
	(void) doc;

	std::string msg;
#ifdef UPNP_DEBUG
	if (errorString == NULL || *errorString == 0) {
		errorString = "Not available";
	}
	if (errorCode > 0) {
		std::cerr << "CUPnPLib::processUPnPErrorMessage() Error: " <<
			message <<
			": Error code :'";
		if (doc) {
			CUPnPError e(*this, doc);
			std::cerr << e.getErrorCode() <<
				"', Error description :'" <<
				e.getErrorDescription() <<
				"'.";
		} else {
			std::cerr << errorCode <<
				"', Error description :'" <<
				errorString <<
				"'.";
		}
		std::cerr << std::endl;
	} else {
		std::cerr << "CUPnPLib::processUPnPErrorMessage() Error: " <<
			message <<
			": UPnP SDK error: " <<
			GetUPnPErrorMessage(errorCode) << 
			" (" << errorCode << ").";
		std::cerr << std::endl;
	}
#endif
	return msg;
}


void CUPnPLib::ProcessActionResponse(
	IXML_Document *RespDoc,
	const std::string &actionName) const
{
	/* remove unused parameter warnings */
	(void) actionName;

	IXML_Element *root = Element_GetRootElement(RespDoc);
	IXML_Element *child = Element_GetFirstChild(root);
	if (child) {
		while (child) {
			const DOMString childTag = Element_GetTag(child);
			std::string childValue = Element_GetTextValue(child);
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPLib::ProcessActionResponse() \n    " <<
				childTag << "='" <<
				childValue << "'";
#endif
			//add the variable to the wanservice property map
			(m_ctrlPoint.m_WanService->propertyMap)[std::string(childTag)] = std::string(childValue);
			child = Element_GetNextSibling(child);
		}
	} else {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPLib::ProcessActionResponse() \n    Empty response for action '" <<
			actionName << "'.";
#endif
	}
#ifdef UPNP_DEBUG
	std::cerr << std::endl;
#endif
}


/*!
 * \brief Returns the root node of a given document.
 */
IXML_Element *CUPnPLib::Element_GetRootElement(
	IXML_Document *doc) const
{
	IXML_Element *root = REINTERPRET_CAST(IXML_Element *)(
		ixmlNode_getFirstChild(
			REINTERPRET_CAST(IXML_Node *)(doc)));

	return root;
}


/*!
 * \brief Returns the first child of a given element.
 */
IXML_Element *CUPnPLib::Element_GetFirstChild(
	IXML_Element *parent) const
{
	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(parent);
	IXML_Node *child = ixmlNode_getFirstChild(node);

	return REINTERPRET_CAST(IXML_Element *)(child);
}


/*!
 * \brief Returns the next sibling of a given child.
 */
IXML_Element *CUPnPLib::Element_GetNextSibling(
	IXML_Element *child) const
{
	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(child);
	IXML_Node *sibling = ixmlNode_getNextSibling(node);

	return REINTERPRET_CAST(IXML_Element *)(sibling);
}


/*!
 * \brief Returns the element tag (name)
 */
const DOMString CUPnPLib::Element_GetTag(
	IXML_Element *element) const
{
	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element);
	const DOMString tag = ixmlNode_getNodeName(node);

	return tag;
}


/*!
 * \brief Returns the TEXT node value of the current node.
 */
const std::string CUPnPLib::Element_GetTextValue(
	IXML_Element *element) const
{
	if (!element) {
		return stdEmptyString;
	}
	IXML_Node *text = ixmlNode_getFirstChild(
		REINTERPRET_CAST(IXML_Node *)(element));
	const DOMString s = ixmlNode_getNodeValue(text);
	std::string ret;
	if (s) {
		ret = s;
	}

	return ret;
}


/*!
 * \brief Returns the TEXT node value of the first child matching tag.
 */
const std::string CUPnPLib::Element_GetChildValueByTag(
	IXML_Element *element,
	const DOMString tag) const
{
	IXML_Element *child =
		Element_GetFirstChildByTag(element, tag);

	return Element_GetTextValue(child);
}


/*!
 * \brief Returns the first child element that matches the requested tag or
 * NULL if not found.
 */
IXML_Element *CUPnPLib::Element_GetFirstChildByTag(
	IXML_Element *element,
	const DOMString tag) const
{
	if (!element || !tag) {
		return NULL;
	}
	
	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element);
	IXML_Node *child = ixmlNode_getFirstChild(node);
	const DOMString childTag = ixmlNode_getNodeName(child);
	while(child && childTag && strcmp(tag, childTag)) {
		child = ixmlNode_getNextSibling(child);
		childTag = ixmlNode_getNodeName(child);
	}

	return REINTERPRET_CAST(IXML_Element *)(child);
}


/*!
 * \brief Returns the next sibling element that matches the requested tag. Should be
 * used with the return value of Element_GetFirstChildByTag().
 */
IXML_Element *CUPnPLib::Element_GetNextSiblingByTag(
	IXML_Element *element, const DOMString tag) const
{
	if (!element || !tag) {
		return NULL;
	}
	
	IXML_Node *child = REINTERPRET_CAST(IXML_Node *)(element);
	const DOMString childTag = NULL;
	do {
		child = ixmlNode_getNextSibling(child);
		childTag = ixmlNode_getNodeName(child);
	} while(child && childTag && strcmp(tag, childTag));

	return REINTERPRET_CAST(IXML_Element *)(child);
}


const std::string CUPnPLib::Element_GetAttributeByTag(
	IXML_Element *element, const DOMString tag) const
{
	IXML_NamedNodeMap *NamedNodeMap = ixmlNode_getAttributes(
		REINTERPRET_CAST(IXML_Node *)(element));
	IXML_Node *attribute = ixmlNamedNodeMap_getNamedItem(NamedNodeMap, tag);
	const DOMString s = ixmlNode_getNodeValue(attribute);
	std::string ret;
	if (s) {
		ret = s;
	}
	ixmlNamedNodeMap_free(NamedNodeMap);

	return ret;
}


CUPnPError::CUPnPError(
	const CUPnPLib &upnpLib,
	IXML_Document *errorDoc)
:
m_root            (upnpLib.Element_GetRootElement(errorDoc)),
m_ErrorCode       (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")),
m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription"))
{
}


CUPnPArgument::CUPnPArgument(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *argument,
	const std::string &/*SCPDURL*/)
:
m_UPnPControlPoint(upnpControlPoint),
m_name                (upnpLib.Element_GetChildValueByTag(argument, "name")),
m_direction           (upnpLib.Element_GetChildValueByTag(argument, "direction")),
m_retval              (upnpLib.Element_GetFirstChildByTag(argument, "retval")),
m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable"))
{

#ifdef UPNP_DEBUG
	std::cerr <<	"CUPnPArgument::CUPnPArgument() \n    Argument:"                  <<
		"\n        name: "                 << m_name <<
		"\n        direction: "            << m_direction <<
		"\n        retval: "               << m_retval <<
		"\n        relatedStateVariable: " << m_relatedStateVariable;
	std::cerr << std::endl;
#endif
}


CUPnPAction::CUPnPAction(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *action,
	const std::string &SCPDURL)
:
m_UPnPControlPoint(upnpControlPoint),
m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL),
m_name(upnpLib.Element_GetChildValueByTag(action, "name"))
{
#ifdef UPNP_DEBUG
	std::cerr <<	"CUPnPAction::CUPnPAction() \n    Action:"    <<
		"\n        name: " << m_name;
	std::cerr << std::endl;
#endif
}


CUPnPAllowedValue::CUPnPAllowedValue(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *allowedValue,
	const std::string &/*SCPDURL*/)
:
m_UPnPControlPoint(upnpControlPoint),
m_allowedValue(upnpLib.Element_GetTextValue(allowedValue))
{
#ifdef UPNP_DEBUG
	std::cerr <<	"CUPnPAllowedValue::CUPnPAllowedValue() \n    AllowedValue:"      <<
		"\n        allowedValue: " << m_allowedValue;
	std::cerr << std::endl;
#endif
}


CUPnPStateVariable::CUPnPStateVariable(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *stateVariable,
	const std::string &SCPDURL)
:
m_UPnPControlPoint(upnpControlPoint),
m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL),
m_name        (upnpLib.Element_GetChildValueByTag(stateVariable, "name")),
m_dataType    (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")),
m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")),
m_sendEvents  (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents"))
{
#ifdef UPNP_DEBUG
	std::cerr <<	"CUPnPStateVariable::CUPnPStateVariable() \n    StateVariable:"     <<
		"\n        name: "         << m_name <<
		"\n        dataType: "     << m_dataType <<
		"\n        defaultValue: " << m_defaultValue <<
		"\n        sendEvents: "   << m_sendEvents;
	std::cerr << std::endl;
#endif
}


CUPnPSCPD::CUPnPSCPD(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *scpd,
	const std::string &SCPDURL)
:
m_UPnPControlPoint(upnpControlPoint),
m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL),
m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL),
m_SCPDURL(SCPDURL)
{
}


CUPnPArgumentValue::CUPnPArgumentValue()
:
m_argument(),
m_value()
{
}


CUPnPArgumentValue::CUPnPArgumentValue(
	const std::string &argument, const std::string &value)
:
m_argument(argument),
m_value(value)
{
}


CUPnPService::CUPnPService(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *service,
	const std::string &URLBase)
:
m_UPnPControlPoint(upnpControlPoint),
m_upnpLib(upnpLib),
m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")),
m_serviceId  (upnpLib.Element_GetChildValueByTag(service, "serviceId")),
m_SCPDURL    (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")),
m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")),
m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")),
m_timeout(1801),
m_SCPD(NULL)
{
	int errcode;
	
	std::vector<char> vscpdURL(URLBase.length() + m_SCPDURL.length() + 1);
	char *scpdURL = &vscpdURL[0];
	errcode = UpnpResolveURL(
		URLBase.c_str(),
		m_SCPDURL.c_str(),
		scpdURL);
	if( errcode != UPNP_E_SUCCESS ) {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::CUPnPService() Error generating scpdURL from " <<
			"|" << URLBase << "|" <<
			m_SCPDURL << "|.";
		std::cerr << std::endl;
#endif
	} else {
		m_absSCPDURL = scpdURL;
	}

	std::vector<char> vcontrolURL(
		URLBase.length() + m_controlURL.length() + 1);
	char *controlURL = &vcontrolURL[0];
	errcode = UpnpResolveURL(
		URLBase.c_str(),
		m_controlURL.c_str(),
		controlURL);
	if( errcode != UPNP_E_SUCCESS ) {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::CUPnPService() Error generating controlURL from " <<
			"|" << URLBase << "|" <<
			m_controlURL << "|.";
		std::cerr << std::endl;
#endif
	} else {
		m_absControlURL = controlURL;
	}

	std::vector<char> veventURL(
		URLBase.length() + m_eventSubURL.length() + 1);
	char *eventURL = &veventURL[0];
	errcode = UpnpResolveURL(
		URLBase.c_str(),
		m_eventSubURL.c_str(),
		eventURL);
	if( errcode != UPNP_E_SUCCESS ) {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::CUPnPService() Error generating eventURL from " <<
			"|" << URLBase << "|" <<
			m_eventSubURL << "|.";
		std::cerr << std::endl;
#endif
	} else {
		m_absEventSubURL = eventURL;
	}

#ifdef UPNP_DEBUG
	std::cerr <<	"CUPnPService::CUPnPService() \n    Service:"             <<
		"\n        serviceType: "    << m_serviceType <<
		"\n        serviceId: "      << m_serviceId <<
		"\n        SCPDURL: "        << m_SCPDURL <<
		"\n        absSCPDURL: "     << m_absSCPDURL <<
		"\n        controlURL: "     << m_controlURL <<
		"\n        absControlURL: "  << m_absControlURL <<
		"\n        eventSubURL: "    << m_eventSubURL <<
		"\n        absEventSubURL: " << m_absEventSubURL;
	std::cerr << std::endl;
#endif

	if (m_serviceType == upnpLib.UPNP_SERVICE_WAN_IP_CONNECTION ||
	    m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION) {
#if 0
	    m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION ||
	    m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG ||
	    m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) {
#endif
#if 0
//#warning Delete this code on release.
		if (!upnpLib.m_ctrlPoint.WanServiceDetected()) {
			// This condition can be used to suspend the parse
			// of the XML tree.
#endif
//#warning Delete this code when m_WanService is no longer used.
			upnpLib.m_ctrlPoint.SetWanService(this);
			// Log it
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPService::CUPnPService() WAN Service Detected: '" <<
				m_serviceType << "'.";
			std::cerr  << std::endl;
#endif
			// Subscribe
			upnpLib.m_ctrlPoint.Subscribe(*this);
#if 0
//#warning Delete this code on release.
		} else {
#ifdef UPNP_DEBUG
			std::cerr << "WAN service detected again: '" <<
				m_serviceType <<
				"'. Will only use the first instance.";
			std::cerr << std::endl;
#endif
		}
#endif
	} else {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::CUPnPService() Uninteresting service detected: '" <<
			m_serviceType << "'. Ignoring.";
		std::cerr << std::endl;
#endif
	}
}


CUPnPService::~CUPnPService()
{
}


bool CUPnPService::Execute(
	const std::string &ActionName,
	const std::vector<CUPnPArgumentValue> &ArgValue) const
{
#ifdef UPNP_DEBUG
	std::cerr << "CUPnPService::Execute() called." << std::endl;
#endif
	if (m_SCPD.get() == NULL) {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::Execute() Service without SCPD Document, cannot execute action '" << ActionName <<
			"' for service '" << GetServiceType() << "'.";
		std::cerr << std::endl;
#endif
		return false;
	}
#ifdef UPNP_DEBUG
	std::cerr << "CUPnPService::Execute() Sending action " << std::endl;
#endif
	// Check for correct action name
	ActionList::const_iterator itAction =
		m_SCPD->GetActionList().find(ActionName);
	if (itAction == m_SCPD->GetActionList().end()) {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::Execute() invalid action name '" << ActionName <<
			"' for service '" << GetServiceType() << "'.";
		std::cerr << std::endl;
#endif
		return false;
	}
#ifdef UPNP_DEBUG
        std::cerr <<  ActionName << "(";
#endif
        bool firstTime = true;
	// Check for correct Argument/Value pairs
	const CUPnPAction &action = *(itAction->second);
	for (unsigned int i = 0; i < ArgValue.size(); ++i) {
		ArgumentList::const_iterator itArg =
			action.GetArgumentList().find(ArgValue[i].GetArgument());
		if (itArg == action.GetArgumentList().end()) {
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPService::Execute() Invalid argument name '" << ArgValue[i].GetArgument() <<
				"' for action '" << action.GetName() <<
				"' for service '" << GetServiceType() << "'.";
			std::cerr << std::endl;
#endif
			return false;
		}
		const CUPnPArgument &argument = *(itArg->second);
		if (tolower(argument.GetDirection()[0]) != 'i' ||
		    tolower(argument.GetDirection()[1]) != 'n') {
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPService::Execute() Invalid direction for argument '" <<
				ArgValue[i].GetArgument() <<
				"' for action '" << action.GetName() <<
				"' for service '" << GetServiceType() << "'.";
			std::cerr << std::endl;
#endif
			return false;
		}
		const std::string relatedStateVariableName =
			argument.GetRelatedStateVariable();
		if (!relatedStateVariableName.empty()) {
			ServiceStateTable::const_iterator itSVT =
				m_SCPD->GetServiceStateTable().
				find(relatedStateVariableName);
			if (itSVT == m_SCPD->GetServiceStateTable().end()) {
#ifdef UPNP_DEBUG
				std::cerr << "CUPnPService::Execute() Inconsistent Service State Table, did not find '" <<
					relatedStateVariableName <<
					"' for argument '" << argument.GetName() <<
					"' for action '" << action.GetName() <<
					"' for service '" << GetServiceType() << "'.";
				std::cerr << std::endl;
#endif
				return false;
			}
			const CUPnPStateVariable &stateVariable = *(itSVT->second);
			if (	!stateVariable.GetAllowedValueList().empty() &&
				stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) ==
					stateVariable.GetAllowedValueList().end()) {
#ifdef UPNP_DEBUG
				std::cerr << "CUPnPService::Execute() Value not allowed '" << ArgValue[i].GetValue() <<
					"' for state variable '" << relatedStateVariableName <<
					"' for argument '" << argument.GetName() <<
					"' for action '" << action.GetName() <<
					"' for service '" << GetServiceType() << "'.";
#endif

				return false;
			}
		}
		if (firstTime) {
			firstTime = false;
		} else {
#ifdef UPNP_DEBUG
			std::cerr << ", ";
#endif
		}
#ifdef UPNP_DEBUG
		std::cerr <<
			ArgValue[i].GetArgument() <<
			"='" <<
			ArgValue[i].GetValue() <<
			"'";
#endif
	}
#ifdef UPNP_DEBUG
        std::cerr << ")" << std::endl;
#endif
	// Everything is ok, make the action
	IXML_Document *ActionDoc = NULL;
	if (ArgValue.size()) {
		for (unsigned int i = 0; i < ArgValue.size(); ++i) {
			int ret = UpnpAddToAction(
				&ActionDoc,
				action.GetName().c_str(),
				GetServiceType().c_str(),
				ArgValue[i].GetArgument().c_str(),
				ArgValue[i].GetValue().c_str());
			if (ret != UPNP_E_SUCCESS) {
				m_upnpLib.processUPnPErrorMessage(
					"UpnpAddToAction", ret, NULL, NULL);
				return false;
			}
		}
	} else {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::Execute() UpnpMakeAction" << std::endl;
#endif
		ActionDoc = UpnpMakeAction(
			action.GetName().c_str(),
			GetServiceType().c_str(),
			0, NULL);
		if (!ActionDoc) {
#ifdef UPNP_DEBUG
                        std::cerr << "CUPnPService::Execute() Error: UpnpMakeAction returned NULL." << std::endl;
#endif
			return false;
		}
	}

	// Send the action asynchronously
	UpnpSendActionAsync(
		m_UPnPControlPoint.GetUPnPClientHandle(),
		GetAbsControlURL().c_str(),
		GetServiceType().c_str(),
		NULL, ActionDoc,
		static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
		NULL);
	return true;
}


const std::string CUPnPService::GetStateVariable(
	const std::string &stateVariableName)
{
	std::map<std::string, std::string>::iterator it;
	it = propertyMap.find(stateVariableName);
	if  (it != propertyMap.end()) {
#ifdef UPNP_DEBUG
            std::cerr << "CUPnPService::GetStateVariable(" << stateVariableName << ") = " << (*it).second << std::endl;
#endif
	    return (*it).second;
	} else {
	    //property map is not populated with the specified value.
	    //we will try to get it with an event

	    //this getvar is just to make the event happening
	    DOMString StVarVal;
	    UpnpGetServiceVarStatus(
		    m_UPnPControlPoint.GetUPnPClientHandle(),
		    GetAbsControlURL().c_str(),
		    stateVariableName.c_str(),
		    &StVarVal);
	    if (StVarVal != NULL) {
		std::string varValue = std::string(StVarVal);
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::GetStateVariable() varValue returned by UpnpGetServiceVarStatus : " << varValue << std::endl;
#endif
		return varValue;
	    } else {
		//use event to get state variable
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPService::GetStateVariable() pausing in case of an UPnP event incomming.";
#endif
		time_t begin_time = time(NULL);
		while (true) {
		    if (time(NULL) - begin_time > 7) {
			break;
		    }
		}
	    }

	    //propertyMap should be populated by nom
	    it = propertyMap.find(stateVariableName);
	    if  (it != propertyMap.end()) {
#ifdef UPNP_DEBUG
                std::cerr << "CUPnPService::GetStateVariable(" << stateVariableName << ") = " << (*it).second << std::endl;
#endif
		return (*it).second;
	    } else {
#ifdef UPNP_DEBUG
                std::cerr << "CUPnPService::GetStateVariable(" << stateVariableName << ") = " << "Empty String" << std::endl;
#endif
		return stdEmptyString;
	    }
	}
}


CUPnPDevice::CUPnPDevice(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *device,
	const std::string &URLBase)
:
m_UPnPControlPoint(upnpControlPoint),
m_DeviceList(upnpControlPoint, upnpLib, device, URLBase),
m_ServiceList(upnpControlPoint, upnpLib, device, URLBase),
m_deviceType       (upnpLib.Element_GetChildValueByTag(device, "deviceType")),
m_friendlyName     (upnpLib.Element_GetChildValueByTag(device, "friendlyName")),
m_manufacturer     (upnpLib.Element_GetChildValueByTag(device, "manufacturer")),
m_manufacturerURL  (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")),
m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")),
m_modelName        (upnpLib.Element_GetChildValueByTag(device, "modelName")),
m_modelNumber      (upnpLib.Element_GetChildValueByTag(device, "modelNumber")),
m_modelURL         (upnpLib.Element_GetChildValueByTag(device, "modelURL")),
m_serialNumber     (upnpLib.Element_GetChildValueByTag(device, "serialNumber")),
m_UDN              (upnpLib.Element_GetChildValueByTag(device, "UDN")),
m_UPC              (upnpLib.Element_GetChildValueByTag(device, "UPC")),
m_presentationURL  (upnpLib.Element_GetChildValueByTag(device, "presentationURL"))
{
	int presURLlen = strlen(URLBase.c_str()) +
		strlen(m_presentationURL.c_str()) + 2;
	std::vector<char> vpresURL(presURLlen);
	char* presURL = &vpresURL[0];
	int errcode = UpnpResolveURL(
		URLBase.c_str(),
		m_presentationURL.c_str(),
		presURL);
	if (errcode != UPNP_E_SUCCESS) {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPDevice::CUPnPDevice() Error generating presentationURL from " <<
			"|" << URLBase << "|" <<
			m_presentationURL << "|.";
		std::cerr << std::endl;
#endif
	} else {
		m_presentationURL = presURL;
	}
	
#ifdef UPNP_DEBUG
	std::cerr <<	"CUPnPDevice::CUPnPDevice() \n    Device: "                <<
		"\n        friendlyName: "      << m_friendlyName <<
		"\n        deviceType: "        << m_deviceType <<
		"\n        manufacturer: "      << m_manufacturer <<
		"\n        manufacturerURL: "   << m_manufacturerURL <<
		"\n        modelDescription: "  << m_modelDescription <<
		"\n        modelName: "         << m_modelName <<
		"\n        modelNumber: "       << m_modelNumber <<
		"\n        modelURL: "          << m_modelURL <<
		"\n        serialNumber: "      << m_serialNumber <<
		"\n        UDN: "               << m_UDN <<
		"\n        UPC: "               << m_UPC <<
		"\n        presentationURL: "   << m_presentationURL
		<< std::endl;
#endif
}


CUPnPRootDevice::CUPnPRootDevice(
	const CUPnPControlPoint &upnpControlPoint,
	CUPnPLib &upnpLib,
	IXML_Element *rootDevice,
	const std::string &OriginalURLBase,
	const std::string &FixedURLBase,
	const char *location,
	int expires)
:
CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase),
m_UPnPControlPoint(upnpControlPoint),
m_URLBase(OriginalURLBase),
m_location(location),
m_expires(expires)
{
#ifdef UPNP_DEBUG
	std::cerr <<
		"CUPnPRootDevice::CUPnPRootDevice() \n    Root Device: "       <<
		"\n        URLBase: "       << m_URLBase <<
		"\n        Fixed URLBase: " << FixedURLBase <<
		"\n        location: "      << m_location <<
		"\n        expires: "       << m_expires
		<< std::endl;
#endif
}


CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL;


CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort)
:
m_upnpLib(*this),
m_UPnPClientHandle(),
m_RootDeviceMap(),
m_ServiceMap(),
m_ActivePortMappingsMap(),
m_RootDeviceListMutex("UPnPControlPoint-RootDeviceList"),
m_IGWDeviceDetected(false),
m_WaitForSearchTimeoutMutex("UPnPControlPoint-WaitForSearchTimeout"),
m_WanService(NULL)
{
#ifdef UPNP_DEBUG
	std::cerr << "UPnPControlPoint::CUPnPControlPoint() Constructor" << std::endl;
#endif
	// Pointer to self
	s_CtrlPoint = this;
	
	// Start UPnP
	int ret;
	char *ipAddress = NULL;
	unsigned short port = 0;
#ifdef UPNP_DEBUG
	int resLog = UpnpInitLog();
	std::cerr << "UPnPControlPoint::CUPnPControlPoint() Init log : " << resLog << std::endl;
#endif
	ret = UpnpInit(ipAddress, udpPort);
#ifdef UPNP_DEBUG
	std::cerr << "CUPnPControlPoint Constructor UpnpInit finished" << std::endl;
#endif
	if (ret != UPNP_E_SUCCESS && ret !=UPNP_E_INIT) {
#ifdef UPNP_DEBUG
		std::cerr << "UPnPControlPoint::CUPnPControlPoint() error(UpnpInit): Error code : ";
#endif
		goto error;
	}
	port = UpnpGetServerPort();
	ipAddress = UpnpGetServerIpAddress();
#ifdef UPNP_DEBUG
	std::cerr << "UPnPControlPoint::CUPnPControlPoint() bound to " << ipAddress << ":" <<
		port << "." << std::endl;
#else
	// unused variable
	(void)port;
#endif

	ret = UpnpRegisterClient(
		static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
		&m_UPnPClientHandle,
		&m_UPnPClientHandle);
	if (ret != UPNP_E_SUCCESS) {
#ifdef UPNP_DEBUG
		std::cerr << "UPnPControlPoint::CUPnPControlPoint() error(UpnpRegisterClient): Error registering callback: ";
#endif
		goto error;
	}

	// We could ask for just the right device here. If the root device
	// contains the device we want, it will respond with the full XML doc,
	// including the root device and every sub-device it has.
	// 
	// But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
	//
	// We should not search twice, because this will produce two
	// UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
	// on the mutex.
//	UpnpSetContentLength(m_UPnPClientHandle, 5000000);
//	UpnpSetMaxContentLength(5000000);
	ret = UpnpSearchAsync(m_UPnPClientHandle, 20, m_upnpLib.UPNP_DEVICE_IGW.c_str(), NULL);
	//ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
	//ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
	//ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
	if (ret != UPNP_E_SUCCESS) {
#ifdef UPNP_DEBUG
		std::cerr << "UPnPControlPoint::CUPnPControlPoint() error(UpnpSearchAsync): Error sending search request: ";
#endif
		goto error;
	}

	// Wait for the UPnP initialization to complete.
	{
		// Lock the search timeout mutex
		m_WaitForSearchTimeoutMutex.lock();

		// Lock it again, so that we block. Unlocking will only happen
		// when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
		// callback.
#ifdef UPNP_DEBUG
		std::cerr << "UPnPControlPoint::CUPnPControlPoint() blocking m_WaitForSearchTimeoutMutex." << std::endl;
#endif
		//RsMutex toto(m_WaitForSearchTimeoutMutex);
		m_WaitForSearchTimeoutMutex.lock();
#ifdef UPNP_DEBUG
		std::cerr << "UPnPControlPoint::CUPnPControlPoint() m_WaitForSearchTimeoutMutex blocking finished." << std::endl;
#endif
	}

	//clean the PortMappingNumberOfEntries as it is erroneus on the first event with the french neufbox
	if (WanServiceDetected()) {
	    m_WanService->propertyMap.erase("PortMappingNumberOfEntries");
	}

#ifdef UPNP_DEBUG
	std::cerr << "UPnPControlPoint::CUPnPControlPoint() CUPnPControlPoint Constructor finished" << std::endl;
#endif
	return;

	// Error processing
error:
#ifdef UPNP_DEBUG
	std::cerr << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << "." << std::endl;
#endif
	UpnpFinish();
#ifdef UPNP_DEBUG
	std::cerr << "UPnPControlPoint::CUPnPControlPoint() UpnpFinish called within CUPnPControlPoint constructor." << std::endl;
#endif
	return;
}


char* CUPnPControlPoint::getInternalIpAddress()
{
    char * addr = UpnpGetServerIpAddress();
    if (addr == NULL) {
        addr = new char[10] ;
		  sprintf(addr,"%s","127.0.0.1");
    }
    return addr;
}

CUPnPControlPoint::~CUPnPControlPoint()
{
	for(	RootDeviceMap::iterator it = m_RootDeviceMap.begin();
		it != m_RootDeviceMap.end();
		++it) {
		delete it->second;
	}
	// Remove all first
	// RemoveAll();
	UpnpUnRegisterClient(m_UPnPClientHandle);
#ifdef UPNP_DEBUG
	std::cerr << "CUPnPControlPoint::~CUPnPControlPoint() UpnpFinish called within CUPnPControlPoint destructor." << std::endl;
#endif
	UpnpFinish();
}


bool CUPnPControlPoint::AddPortMappings(
	std::vector<CUPnPPortMapping> &upnpPortMapping)
{
	if (!WanServiceDetected()) {
#ifdef UPNP_DEBUG
		std::cerr <<  "CUPnPControlPoint::AddPortMappings() UPnP Error: "
			"CUPnPControlPoint::AddPortMapping: "
			"WAN Service not detected." << std::endl;
#endif
		return false;
	}
#ifdef UPNP_DEBUG
	std::cerr <<  "CUPnPControlPoint::AddPortMappings() called." << std::endl;
#endif

	int n = upnpPortMapping.size();
	bool ok = false;

	// Check the number of port mappings before
	//have a little break in case we just modified the variable, so we have to wait for an event
//	std::cerr << "GetStateVariable pausing in case of an UPnP event incomming.";
//	time_t begin_time = time(NULL);
//	while (true) {
//	   if (time(NULL) - begin_time > 7) {
//	       break;
//	   }
//	}
	std::istringstream OldPortMappingNumberOfEntries(
		m_WanService->GetStateVariable(
			"PortMappingNumberOfEntries"));
	int oldNumberOfEntries;
	OldPortMappingNumberOfEntries >> oldNumberOfEntries;

	// Add the enabled port mappings
	for (int i = 0; i < n; ++i) {
		if (upnpPortMapping[i].getEnabled() == "1") {
			// Add the mapping to the control point 
			// active mappings list
			m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] =
				upnpPortMapping[i];
			
			// Add the port mapping
			PrivateAddPortMapping(upnpPortMapping[i]);
		}
	}

	// Not very good, must find a better test : check the new number of port entries
	//have a little break in case we just modified the variable, so we have to wait for an event
#ifdef UPNP_DEBUG
	std::cerr << "CUPnPControlPoint::AddPortMappings() GetStateVariable pausing in case of an UPnP event incomming.";
#endif
	time_t begin_time = time(NULL);
	while (true) {
	   if (time(NULL) - begin_time > 4) {
	       break;
	   }
	}
	std::istringstream NewPortMappingNumberOfEntries(
		m_WanService->GetStateVariable(
			"PortMappingNumberOfEntries"));
	int newNumberOfEntries;
	NewPortMappingNumberOfEntries >> newNumberOfEntries;
#ifdef UPNP_DEBUG
	std::cerr <<  "CUPnPControlPoint::AddPortMappings() CUPnPControlPoint::AddPortMappings() newNumberOfEntries - oldNumberOfEntries : " << (newNumberOfEntries - oldNumberOfEntries) << std::endl;
#endif
	ok = newNumberOfEntries - oldNumberOfEntries >= 1;

#ifdef UPNP_DEBUG
	std::cerr <<  "CUPnPControlPoint::AddPortMappings() finished. Success = " << ok << std::endl;
#endif

	return ok;
}

std::string CUPnPControlPoint::getExternalAddress()
{
	if (!WanServiceDetected()) {
#ifdef UPNP_DEBUG
		std::cerr <<  "CUPnPControlPoint::getExternalAddress() UPnP Error: "
			"CUPnPControlPoint::AddPortMapping: "
			"WAN Service not detected." << std::endl;
#endif
                return "";
	}
	std::string result =  m_WanService->GetStateVariable("NewExternalIPAddress");
#ifdef UPNP_DEBUG
	std::cerr <<  "CUPnPControlPoint::getExternalAddress() m_WanService->GetStateVariable(NewExternalIPAddress) = " <<  result << std::endl;
#endif
	if (result == "") {
	    PrivateGetExternalIpAdress();
	    result =  m_WanService->GetStateVariable("NewExternalIPAddress");
#ifdef UPNP_DEBUG
	    std::cerr <<  "CUPnPControlPoint::getExternalAddress() m_WanService->GetStateVariable(NewExternalIPAddress) = " <<  result << std::endl;
#endif
	    if (result == "") {
		result = m_WanService->GetStateVariable("ExternalIPAddress");
#ifdef UPNP_DEBUG
		std::cerr <<  "CUPnPControlPoint::getExternalAddress() m_WanService->GetStateVariable(ExternalIPAddress) = " <<  result << std::endl;
#endif
	    }
	}
	return result;
}

void CUPnPControlPoint::RefreshPortMappings()
{
	for (	PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
		it != m_ActivePortMappingsMap.end();
		++it) {
		PrivateAddPortMapping(it->second);
	}
}


bool CUPnPControlPoint::PrivateAddPortMapping(
	CUPnPPortMapping &upnpPortMapping)
{
	// Get an IP address. The UPnP server one must do.
	std::string ipAddress(UpnpGetServerIpAddress());
	
	// Start building the action
	std::string actionName("AddPortMapping");
	std::vector<CUPnPArgumentValue> argval(8);
	
	// Action parameters
	argval[0].SetArgument("NewRemoteHost");
	argval[0].SetValue("");
	argval[1].SetArgument("NewExternalPort");
	argval[1].SetValue(upnpPortMapping.getExPort());
	argval[2].SetArgument("NewProtocol");
	argval[2].SetValue(upnpPortMapping.getProtocol());
	argval[3].SetArgument("NewInternalPort");
	argval[3].SetValue(upnpPortMapping.getInPort());
	argval[4].SetArgument("NewInternalClient");
	argval[4].SetValue(ipAddress);
	argval[5].SetArgument("NewEnabled");
	argval[5].SetValue("1");
	argval[6].SetArgument("NewPortMappingDescription");
	argval[6].SetValue(upnpPortMapping.getDescription());
	argval[7].SetArgument("NewLeaseDuration");
	argval[7].SetValue("0");
	
	// Execute
	bool ret = true;
	for (ServiceMap::iterator it = m_ServiceMap.begin();
	     it != m_ServiceMap.end(); ++it) {
		ret &= it->second->Execute(actionName, argval);
	}

	return ret;
}


bool CUPnPControlPoint::DeletePortMappings(
	std::vector<CUPnPPortMapping> &upnpPortMapping)
{
#ifdef UPNP_DEBUG
	std::cerr << "CUPnPControlPoint::DeletePortMappings() called." << std::endl;
#endif
	if (!WanServiceDetected()) {
#ifdef UPNP_DEBUG
		std::cerr  <<  "UPnP Error: "
			"CUPnPControlPoint::DeletePortMapping: "
			"WAN Service not detected." << std::endl;
#endif
		return false;
	}
	
	int n = upnpPortMapping.size();
	
	// Delete the enabled port mappings
	for (int i = 0; i < n; ++i) {
		if (upnpPortMapping[i].getEnabled() == "1") {
			// Delete the mapping from the control point 
			// active mappings list
			PortMappingMap::iterator it =
				m_ActivePortMappingsMap.find(
					upnpPortMapping[i].getKey());
			if (it != m_ActivePortMappingsMap.end()) {
				m_ActivePortMappingsMap.erase(it);
			} else {
#ifdef UPNP_DEBUG
				std::cerr   <<  "CUPnPControlPoint::DeletePortMappings() UPnP Error: "
					"CUPnPControlPoint::DeletePortMapping: "
					"Mapping was not found in the active "
					"mapping map." << std::endl;
#endif
			}
			
			// Delete the port mapping
			PrivateDeletePortMapping(upnpPortMapping[i]);
		}
	}
		
	return true;
}


bool CUPnPControlPoint::PrivateDeletePortMapping(
	CUPnPPortMapping &upnpPortMapping)
{
	// Start building the action
	std::string actionName("DeletePortMapping");
	std::vector<CUPnPArgumentValue> argval(3);
	
	// Action parameters
	argval[0].SetArgument("NewRemoteHost");
	argval[0].SetValue("");
	argval[1].SetArgument("NewExternalPort");
	argval[1].SetValue(upnpPortMapping.getExPort());
	argval[2].SetArgument("NewProtocol");
	argval[2].SetValue(upnpPortMapping.getProtocol());
	
	// Execute
	bool ret = true;
	for (ServiceMap::iterator it = m_ServiceMap.begin();
	     it != m_ServiceMap.end(); ++it) {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::PrivateDeletePortMapping() Sending a delete port mapping action." << std::endl;
#endif
		ret &= it->second->Execute(actionName, argval);
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::PrivateDeletePortMapping() Delete port mapping action finished." << std::endl;
#endif
	}

	return ret;
}

bool CUPnPControlPoint::PrivateGetExternalIpAdress()
{
	// Start building the action
	std::string actionName("GetExternalIPAddress");
	std::vector<CUPnPArgumentValue> argval(0);

	// Execute
	bool ret = true;
	for (ServiceMap::iterator it = m_ServiceMap.begin();
	     it != m_ServiceMap.end(); ++it) {
		ret &= it->second->Execute(actionName, argval);
	}

	return ret;
}


// This function is static
int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
{
	std::string msg;
	std::string msg2;
	// Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
	// happen with a wrong cookie and... boom!
	// CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
	CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint ;

	if (upnpCP == NULL)
		return 0;
	
	//fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
	switch (EventType) {
	case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Callback() UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
#endif
		goto upnpDiscovery;
	case UPNP_DISCOVERY_SEARCH_RESULT: {
#ifdef UPNP_DEBUG
		std::cerr << "UPNP_DISCOVERY_SEARCH_RESULT: ";
#endif
		// UPnP Discovery
upnpDiscovery:
		struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
		IXML_Document *doc = NULL;
		int ret;
		if (d_event->ErrCode != UPNP_E_SUCCESS) {
#ifdef UPNP_DEBUG
                        std::cerr << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << "." << std::endl;
#endif
		}
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Callback() URetrieving device description from " <<
				d_event->Location << "." << std::endl;
#endif
		// Get the XML tree device description in doc
		ret = UpnpDownloadXmlDoc(d_event->Location, &doc); 
		if (ret != UPNP_E_SUCCESS) {
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPControlPoint::Callback() UError retrieving device description from " <<
				d_event->Location << ": " <<
				upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) << ".";
#endif
		} else {
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPControlPoint::Callback() URetrieving device description from " <<
				d_event->Location << "." << std::endl;
#endif
		}
		if (doc) {
			// Get the root node
			IXML_Element *root =
				upnpCP->m_upnpLib.Element_GetRootElement(doc);
			// Extract the URLBase
			const std::string urlBase = upnpCP->m_upnpLib.
				Element_GetChildValueByTag(root, "URLBase");
			// Get the root device
			IXML_Element *rootDevice = upnpCP->m_upnpLib.
				Element_GetFirstChildByTag(root, "device");
			// Extract the deviceType
			std::string devType(upnpCP->m_upnpLib.
				Element_GetChildValueByTag(rootDevice, "deviceType"));
			// Only add device if it is an InternetGatewayDevice
			if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
				// This condition can be used to auto-detect
				// the UPnP device we are interested in.
				// Obs.: Don't block the entry here on this 
				// condition! There may be more than one device,
				// and the first that enters may not be the one
				// we are interested in!
				upnpCP->SetIGWDeviceDetected(true);
				// Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
				// we don't want to spam our logs.
				//if (EventType != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) {
#ifdef UPNP_DEBUG
					std::cerr << "Internet Gateway Device Detected." << std::endl;
#endif
				//}
#ifdef UPNP_DEBUG
				std::cerr << "CUPnPControlPoint::Callback() UGetting root device desc." << std::endl;
#endif
				// Add the root device to our list
				upnpCP->AddRootDevice(rootDevice, urlBase,
					d_event->Location, d_event->Expires);
#ifdef UPNP_DEBUG
				std::cerr << "CUPnPControlPoint::Callback() UFinishing getting root device desc." << std::endl;
#endif
			}
			// Free the XML doc tree
			ixmlDocument_free(doc);
		}
		break;
	}
	case UPNP_DISCOVERY_SEARCH_TIMEOUT: {
		//fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
		// Search timeout
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Callback() UUPNP_DISCOVERY_SEARCH_TIMEOUT : unlocking mutex." << std::endl;
#endif

		// Unlock the search timeout mutex
		upnpCP->m_WaitForSearchTimeoutMutex.unlock();
		
		break;
	}
	case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: {
		//fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
		// UPnP Device Removed
		struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event;
		if (dab_event->ErrCode != UPNP_E_SUCCESS) {
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPControlPoint::Callback() Uerror(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
				upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) <<
				"." << std::endl;
#endif
		}
		std::string devType = dab_event->DeviceType;
		// Check for an InternetGatewayDevice and removes it from the list
		std::transform(devType.begin(), devType.end(), devType.begin(), tolower);
		if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
			upnpCP->RemoveRootDevice(dab_event->DeviceId);
		}
		break;
	}
	case UPNP_EVENT_RECEIVED: {
#ifdef UPNP_DEBUG
		fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
#endif
		// Event reveived
		struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
		const std::string Sid = e_event->Sid;
		// Parses the event
		upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables);
		break;
	}
	case UPNP_EVENT_SUBSCRIBE_COMPLETE:
		//fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
		msg += "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
		goto upnpEventRenewalComplete;
	case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
		//fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
		msg += "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
		goto upnpEventRenewalComplete;
	case UPNP_EVENT_RENEWAL_COMPLETE: {
		//fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
		msg += "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
upnpEventRenewalComplete:
		struct Upnp_Event_Subscribe *es_event =
			(struct Upnp_Event_Subscribe *)Event;
		if (es_event->ErrCode != UPNP_E_SUCCESS) {
			msg += "Error in Event Subscribe Callback";
			upnpCP->m_upnpLib.processUPnPErrorMessage(
				msg, es_event->ErrCode, NULL, NULL);
		} else {
#if 0
			TvCtrlPointHandleSubscribeUpdate(
				es_event->PublisherUrl,
				es_event->Sid,
				es_event->TimeOut );
#endif
		}
		
		break;
	}
	
	case UPNP_EVENT_AUTORENEWAL_FAILED:
		//fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
		msg += "CUPnPControlPoint::Callback() error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
		msg2 += "UPNP_EVENT_AUTORENEWAL_FAILED: ";
		goto upnpEventSubscriptionExpired;
	case UPNP_EVENT_SUBSCRIPTION_EXPIRED: {
		//fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
		msg += "CUPnPControlPoint::Callback() error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
		msg2 += "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
upnpEventSubscriptionExpired:
		struct Upnp_Event_Subscribe *es_event =
			(struct Upnp_Event_Subscribe *)Event;
		Upnp_SID newSID;
		int TimeOut = 1801;
		int ret = UpnpSubscribe(
			upnpCP->m_UPnPClientHandle,
#ifdef PATCHED_LIBUPNP
			UpnpString_get_String(es_event->PublisherUrl),
#else
			es_event->PublisherUrl,
#endif
			&TimeOut,
			newSID);
		if (ret != UPNP_E_SUCCESS) {
			msg += "Error Subscribing to EventURL";
			upnpCP->m_upnpLib.processUPnPErrorMessage(
				msg, es_event->ErrCode, NULL, NULL);
		} else {
			ServiceMap::iterator it =
#ifdef PATCHED_LIBUPNP
				upnpCP->m_ServiceMap.find(UpnpString_get_String(es_event->PublisherUrl));
#else
				upnpCP->m_ServiceMap.find(es_event->PublisherUrl);
#endif
			if (it != upnpCP->m_ServiceMap.end()) {
				CUPnPService &service = *(it->second);
				service.SetTimeout(TimeOut);
				service.SetSID(newSID);
				std::cerr << "CUPnPControlPoint::Callback() Re-subscribed to EventURL '" <<
#ifdef PATCHED_LIBUPNP
					UpnpString_get_String(es_event->PublisherUrl) <<
#else
					es_event->PublisherUrl <<
#endif
					"' with SID == '" <<
					newSID << "'." << std::endl;
				// In principle, we should test to see if the
				// service is the same. But here we only have one
				// service, so...
				upnpCP->RefreshPortMappings();
			} else {
#ifdef UPNP_DEBUG
				std::cerr << "CUPnPControlPoint::Callback() Error: did not find service " <<
					newSID << " in the service map." << std::endl;
#endif
			}
		}
		break;
	}
	case UPNP_CONTROL_ACTION_COMPLETE: {
		//fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
		// This is here if we choose to do this asynchronously
		struct Upnp_Action_Complete *a_event =
			(struct Upnp_Action_Complete *)Event;
		if (a_event->ErrCode != UPNP_E_SUCCESS) {
			upnpCP->m_upnpLib.processUPnPErrorMessage(
				"UpnpSendActionAsync",
				a_event->ErrCode, NULL,
				a_event->ActionResult);
		} else {
			// Check the response document
			upnpCP->m_upnpLib.ProcessActionResponse(
				a_event->ActionResult,
				"<UpnpSendActionAsync>");
		}
		/* No need for any processing here, just print out results.
		 * Service state table updates are handled by events.
		 */
		break;
	}
	case UPNP_CONTROL_GET_VAR_COMPLETE: {
#ifdef UPNP_DEBUG
		fprintf(stderr, "CUPnPControlPoint::Callback() Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
#endif
		msg += "CUPnPControlPoint::Callback() error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
		struct Upnp_State_Var_Complete *sv_event =
			(struct Upnp_State_Var_Complete *)Event;
		if (sv_event->ErrCode != UPNP_E_SUCCESS) {
			msg += "m_UpnpGetServiceVarStatusAsync";
			upnpCP->m_upnpLib.processUPnPErrorMessage(
				msg, sv_event->ErrCode, NULL, NULL);
		} else {
		    //add the variable to the wanservice property map
		    (upnpCP->m_WanService->propertyMap)[std::string(sv_event->StateVarName)] = std::string(sv_event->CurrentVal);
		}
		break;
	}
	// ignore these cases, since this is not a device 
	case UPNP_CONTROL_GET_VAR_REQUEST:
		//fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Callback() error(UPNP_CONTROL_GET_VAR_REQUEST): ";
#endif
		goto eventSubscriptionRequest;
	case UPNP_CONTROL_ACTION_REQUEST:
		//fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Callback() error(UPNP_CONTROL_ACTION_REQUEST): ";
#endif
		goto eventSubscriptionRequest;
	case UPNP_EVENT_SUBSCRIPTION_REQUEST:
		//fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Callback() error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
#endif
eventSubscriptionRequest:
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Callback() This is not a UPnP Device, this is a UPnP Control Point, event ignored." <<  std::endl;
#endif
		break;
	default:
		// Humm, this is not good, we forgot to handle something...
#ifdef UPNP_DEBUG
		fprintf(stderr,
			"Callback: default... Unknown event:'%d', not good.\n",
			EventType);
		std::cerr << "CUPnPControlPoint::Callback() error(UPnP::Callback): Event not handled:'" <<
			EventType << "'." << std::endl;
#endif
		// Better not throw in the callback. Who would catch it?
		//throw CUPnPException(msg);
		break;
	}
	
	return 0;
}


void CUPnPControlPoint::OnEventReceived(
		const std::string &Sid,
		int EventKey,
		IXML_Document *ChangedVariablesDoc)
{
	/* remove unused parameter warnings */
	(void) EventKey;

#ifdef UPNP_DEBUG
	std::cerr << "CUPnPControlPoint::OnEventReceived() UPNP_EVENT_RECEIVED:" <<
		"\n    SID: " << Sid <<
		"\n    Key: " << EventKey << std::endl;
	std::cerr << "CUPnPControlPoint::OnEventReceived() m_WanService->GetServiceId() : " << m_WanService->GetSID() << std::endl;
#endif

	if (m_WanService->GetSID() == Sid) {
	    //let's store the properties if it is an event of the wan device
#ifdef UPNP_DEBUG
	    std::cerr << "CUPnPControlPoint::OnEventReceived() \n    Property list:";
#endif

	    IXML_Element *root =
		    m_upnpLib.Element_GetRootElement(ChangedVariablesDoc);
	    IXML_Element *child =
		    m_upnpLib.Element_GetFirstChild(root);
	    if (child) {
		    while (child) {
			    IXML_Element *child2 =
				    m_upnpLib.Element_GetFirstChild(child);
			    const DOMString childTag =
				    m_upnpLib.Element_GetTag(child2);
			    std::string childValue =
				    m_upnpLib.Element_GetTextValue(child2);
#ifdef UPNP_DEBUG
			    std::cerr << "\n        " <<
				    childTag << "='" <<
				    childValue << "'";
#endif
			    const std::string cTag(childTag);
			    const std::string cValue(childValue);
			    (m_WanService->propertyMap)[cTag] = cValue;
			    child = m_upnpLib.Element_GetNextSibling(child);
		    }
	    } else {
#ifdef UPNP_DEBUG
		    std::cerr << "CUPnPControlPoint::OnEventReceived() \n    Empty property list.";
#endif
	    }
#ifdef UPNP_DEBUG
	    std::cerr << std::endl;
#endif
	    // Freeing that doc segfaults. Probably should not be freed.
	    //ixmlDocument_free(ChangedVariablesDoc);
	}
}


void CUPnPControlPoint::AddRootDevice(
	IXML_Element *rootDevice, const std::string &urlBase,
	const char *location, int expires)
{
	// Lock the Root Device List
	RsMutex toto(m_RootDeviceListMutex);
	
	// Root node's URLBase
	std::string OriginalURLBase(urlBase);
	std::string FixedURLBase(OriginalURLBase.empty() ?
		location :
		OriginalURLBase);

	// Get the UDN (Unique Device Name)
	std::string UDN(
		m_upnpLib.Element_GetChildValueByTag(rootDevice, "UDN"));
	RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
	bool alreadyAdded = it != m_RootDeviceMap.end();
	if (alreadyAdded) {
		// Just set the expires field
		it->second->SetExpires(expires);
	} else {
		// Add a new root device to the root device list
		CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice(
			*this, m_upnpLib, rootDevice,
			OriginalURLBase, FixedURLBase,
			location, expires);
		m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice;
	}
}


void CUPnPControlPoint::RemoveRootDevice(const char *udn)
{
	// Lock the Root Device List
	RsMutex toto(m_RootDeviceListMutex);

	// Remove
	std::string UDN(udn);
	RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
	if (it != m_RootDeviceMap.end()) {
		delete it->second;
		m_RootDeviceMap.erase(UDN);
	}
}


void CUPnPControlPoint::Subscribe(CUPnPService &service)
{

	IXML_Document *scpdDoc = NULL;
	int errcode = UpnpDownloadXmlDoc(
		service.GetAbsSCPDURL().c_str(), &scpdDoc);
	if (errcode == UPNP_E_SUCCESS) {
		// Get the root node of this service (the SCPD Document)
		IXML_Element *scpdRoot =
			m_upnpLib.Element_GetRootElement(scpdDoc);
		CUPnPSCPD *scpd = new CUPnPSCPD(*this, m_upnpLib,
			scpdRoot, service.GetAbsSCPDURL());
		service.SetSCPD(scpd);
		m_ServiceMap[service.GetAbsEventSubURL()] = &service;
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Subscribe() Successfully retrieved SCPD Document for service " <<
			service.GetServiceType() << ", absEventSubURL: " <<
			service.GetAbsEventSubURL() << "." << std::endl;
#endif

		// Now try to subscribe to this service. If the subscription
		// is not successfull, we will not be notified about events,
		// but it may be possible to use the service anyway.
		errcode = UpnpSubscribe(m_UPnPClientHandle,
			service.GetAbsEventSubURL().c_str(),
			service.GetTimeoutAddr(),
			service.GetSID());
		if (errcode == UPNP_E_SUCCESS) {
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPControlPoint::Subscribe() Successfully subscribed to service " <<
				service.GetServiceType() << ", absEventSubURL: " <<
				service.GetAbsEventSubURL() << "." << std::endl;
#endif
		} else {
#ifdef UPNP_DEBUG
			std::cerr << "CUPnPControlPoint::Subscribe() Error subscribing to service " <<
				service.GetServiceType() << ", absEventSubURL: " <<
				service.GetAbsEventSubURL() << ", error: " <<
				m_upnpLib.GetUPnPErrorMessage(errcode) << ".";
#endif
			goto error;
		}
	} else {
#ifdef UPNP_DEBUG
		std::cerr << "CUPnPControlPoint::Subscribe() Error getting SCPD Document from " <<
			service.GetAbsSCPDURL() << "." << std::endl;
#endif
	}
	
	return;

error:
	;
#ifdef UPNP_DEBUG
	std::cerr << std::endl;
#endif
}


void CUPnPControlPoint::Unsubscribe(CUPnPService &service)
{
	ServiceMap::iterator it = m_ServiceMap.find(service.GetAbsEventSubURL());
	if (it != m_ServiceMap.end()) {
		m_ServiceMap.erase(it);
		UpnpUnSubscribe(m_UPnPClientHandle, service.GetSID());
	}
}


// File_checked_for_headers
