1//
2// This file is part of the aMule Project.
3//
4// Copyright (c) 2004-2011 Marcelo Roberto Jimenez ( phoenix@amule.org )
5// Copyright (c) 2006-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6//
7// Any parts of this program derived from the xMule, lMule or eMule project,
8// or contributed by third-party developers are copyrighted by their
9// respective authors.
10//
11// This program is free software; you can redistribute it and/or modify
12// it under the terms of the GNU General Public License as published by
13// the Free Software Foundation; either version 2 of the License, or
14// (at your option) any later version.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
24//
25
26#ifdef HAVE_CONFIG_H
27#	include "config.h"		// Needed for ENABLE_UPNP
28#endif
29
30#ifdef ENABLE_UPNP
31
32#define UPNP_C
33
34#include "UPnPBase.h"
35
36
37#include <dlfcn.h>		// For dlopen(), dlsym(), dlclose()
38#include <algorithm>		// For transform()
39
40
41#ifdef __GNUC__
42	#if __GNUC__ >= 4
43		#define REINTERPRET_CAST(x) reinterpret_cast<x>
44	#endif
45#endif
46#ifndef REINTERPRET_CAST
47	// Let's hope that function pointers are equal in size to data pointers
48	#define REINTERPRET_CAST(x) (x)
49#endif
50
51
52/**
53 * Case insensitive std::string comparison
54 */
55bool stdStringIsEqualCI(const std::string &s1, const std::string &s2)
56{
57	std::string ns1(s1);
58	std::string ns2(s2);
59	std::transform(ns1.begin(), ns1.end(), ns1.begin(), tolower);
60	std::transform(ns2.begin(), ns2.end(), ns2.begin(), tolower);
61	return ns1 == ns2;
62}
63
64
65CUPnPPortMapping::CUPnPPortMapping(
66	int port,
67	const std::string &protocol,
68	bool enabled,
69	const std::string &description)
70:
71m_port(),
72m_protocol(protocol),
73m_enabled(enabled ? "1" : "0"),
74m_description(description),
75m_key()
76{
77	std::ostringstream oss;
78	oss << port;
79	m_port = oss.str();
80	m_key = m_protocol + m_port;
81}
82
83
84const std::string &CUPnPLib::UPNP_ROOT_DEVICE =
85	"upnp:rootdevice";
86
87const std::string &CUPnPLib::UPNP_DEVICE_IGW =
88	"urn:schemas-upnp-org:device:InternetGatewayDevice:1";
89const std::string &CUPnPLib::UPNP_DEVICE_WAN =
90	"urn:schemas-upnp-org:device:WANDevice:1";
91const std::string &CUPnPLib::UPNP_DEVICE_WAN_CONNECTION =
92	"urn:schemas-upnp-org:device:WANConnectionDevice:1";
93const std::string &CUPnPLib::UPNP_DEVICE_LAN =
94	"urn:schemas-upnp-org:device:LANDevice:1";
95
96const std::string &CUPnPLib::UPNP_SERVICE_LAYER3_FORWARDING =
97	"urn:schemas-upnp-org:service:Layer3Forwarding:1";
98const std::string &CUPnPLib::UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG =
99	"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1";
100const std::string &CUPnPLib::UPNP_SERVICE_WAN_IP_CONNECTION =
101	"urn:schemas-upnp-org:service:WANIPConnection:1";
102const std::string &CUPnPLib::UPNP_SERVICE_WAN_PPP_CONNECTION =
103	"urn:schemas-upnp-org:service:WANPPPConnection:1";
104
105
106CUPnPLib::CUPnPLib(CUPnPControlPoint &ctrlPoint)
107:
108m_ctrlPoint(ctrlPoint)
109{
110}
111
112
113std::string CUPnPLib::GetUPnPErrorMessage(int code) const
114{
115	return UpnpGetErrorMessage(code);
116}
117
118
119std::string CUPnPLib::processUPnPErrorMessage(
120	const std::string &messsage,
121	int errorCode,
122	const DOMString errorString,
123	IXML_Document *doc) const
124{
125	std::ostringstream msg;
126	if (errorString == NULL || *errorString == 0) {
127		errorString = "Not available";
128	}
129	if (errorCode > 0) {
130		msg << "Error: " <<
131			messsage <<
132			": Error code :'";
133		if (doc) {
134			CUPnPError e(*this, doc);
135			msg << e.getErrorCode() <<
136				"', Error description :'" <<
137				e.getErrorDescription() <<
138				"'.";
139		} else {
140			msg << errorCode <<
141				"', Error description :'" <<
142				errorString <<
143				"'.";
144		}
145		AddLogLineU(false, logUPnP, msg);
146	} else {
147		msg << "Error: " <<
148			messsage <<
149			": UPnP SDK error: " <<
150			GetUPnPErrorMessage(errorCode) <<
151			" (" << errorCode << ").";
152		AddLogLineU(false, logUPnP, msg);
153	}
154
155	return msg.str();
156}
157
158
159void CUPnPLib::ProcessActionResponse(
160	IXML_Document *RespDoc,
161	const std::string &actionName) const
162{
163	std::ostringstream msg;
164	msg << "Response: ";
165	IXML_Element *root = Element_GetRootElement(RespDoc);
166	IXML_Element *child = Element_GetFirstChild(root);
167	if (child) {
168		while (child) {
169			const DOMString childTag = Element_GetTag(child);
170			std::string childValue = Element_GetTextValue(child);
171			msg << "\n    " <<
172				childTag << "='" <<
173				childValue << "'";
174			child = Element_GetNextSibling(child);
175		}
176	} else {
177		msg << "\n    Empty response for action '" <<
178			actionName << "'.";
179	}
180	AddDebugLogLineN(logUPnP, msg);
181}
182
183
184/*!
185 * \brief Returns the root node of a given document.
186 */
187IXML_Element *CUPnPLib::Element_GetRootElement(
188	IXML_Document *doc) const
189{
190	IXML_Element *root = REINTERPRET_CAST(IXML_Element *)(
191		ixmlNode_getFirstChild(
192			REINTERPRET_CAST(IXML_Node *)(doc)));
193
194	return root;
195}
196
197
198/*!
199 * \brief Returns the first child of a given element.
200 */
201IXML_Element *CUPnPLib::Element_GetFirstChild(
202	IXML_Element *parent) const
203{
204	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(parent);
205	IXML_Node *child = ixmlNode_getFirstChild(node);
206
207	return REINTERPRET_CAST(IXML_Element *)(child);
208}
209
210
211/*!
212 * \brief Returns the next sibling of a given child.
213 */
214IXML_Element *CUPnPLib::Element_GetNextSibling(
215	IXML_Element *child) const
216{
217	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(child);
218	IXML_Node *sibling = ixmlNode_getNextSibling(node);
219
220	return REINTERPRET_CAST(IXML_Element *)(sibling);
221}
222
223
224/*!
225 * \brief Returns the element tag (name)
226 */
227const DOMString CUPnPLib::Element_GetTag(
228	IXML_Element *element) const
229{
230	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element);
231	const DOMString tag = ixmlNode_getNodeName(node);
232
233	return tag;
234}
235
236
237/*!
238 * \brief Returns the TEXT node value of the current node.
239 */
240const std::string CUPnPLib::Element_GetTextValue(
241	IXML_Element *element) const
242{
243	if (!element) {
244		return stdEmptyString;
245	}
246	IXML_Node *text = ixmlNode_getFirstChild(
247		REINTERPRET_CAST(IXML_Node *)(element));
248	const DOMString s = ixmlNode_getNodeValue(text);
249	std::string ret;
250	if (s) {
251		ret = s;
252	}
253
254	return ret;
255}
256
257
258/*!
259 * \brief Returns the TEXT node value of the first child matching tag.
260 */
261const std::string CUPnPLib::Element_GetChildValueByTag(
262	IXML_Element *element,
263	const DOMString tag) const
264{
265	IXML_Element *child =
266		Element_GetFirstChildByTag(element, tag);
267
268	return Element_GetTextValue(child);
269}
270
271
272/*!
273 * \brief Returns the first child element that matches the requested tag or
274 * NULL if not found.
275 */
276IXML_Element *CUPnPLib::Element_GetFirstChildByTag(
277	IXML_Element *element,
278	const DOMString tag) const
279{
280	if (!element || !tag) {
281		return NULL;
282	}
283
284	IXML_Node *node = REINTERPRET_CAST(IXML_Node *)(element);
285	IXML_Node *child = ixmlNode_getFirstChild(node);
286	const DOMString childTag = ixmlNode_getNodeName(child);
287	while(child && childTag && strcmp(tag, childTag)) {
288		child = ixmlNode_getNextSibling(child);
289		childTag = ixmlNode_getNodeName(child);
290	}
291
292	return REINTERPRET_CAST(IXML_Element *)(child);
293}
294
295
296/*!
297 * \brief Returns the next sibling element that matches the requested tag. Should be
298 * used with the return value of Element_GetFirstChildByTag().
299 */
300IXML_Element *CUPnPLib::Element_GetNextSiblingByTag(
301	IXML_Element *element, const DOMString tag) const
302{
303	if (!element || !tag) {
304		return NULL;
305	}
306
307	IXML_Node *child = REINTERPRET_CAST(IXML_Node *)(element);
308	const DOMString childTag = NULL;
309	do {
310		child = ixmlNode_getNextSibling(child);
311		childTag = ixmlNode_getNodeName(child);
312	} while(child && childTag && strcmp(tag, childTag));
313
314	return REINTERPRET_CAST(IXML_Element *)(child);
315}
316
317
318const std::string CUPnPLib::Element_GetAttributeByTag(
319	IXML_Element *element, const DOMString tag) const
320{
321	IXML_NamedNodeMap *NamedNodeMap = ixmlNode_getAttributes(
322		REINTERPRET_CAST(IXML_Node *)(element));
323	IXML_Node *attribute = ixmlNamedNodeMap_getNamedItem(NamedNodeMap, tag);
324	const DOMString s = ixmlNode_getNodeValue(attribute);
325	std::string ret;
326	if (s) {
327		ret = s;
328	}
329	ixmlNamedNodeMap_free(NamedNodeMap);
330
331	return ret;
332}
333
334
335CUPnPError::CUPnPError(
336	const CUPnPLib &upnpLib,
337	IXML_Document *errorDoc)
338:
339m_root            (upnpLib.Element_GetRootElement(errorDoc)),
340m_ErrorCode       (upnpLib.Element_GetChildValueByTag(m_root, "errorCode")),
341m_ErrorDescription(upnpLib.Element_GetChildValueByTag(m_root, "errorDescription"))
342{
343}
344
345
346CUPnPArgument::CUPnPArgument(
347	const CUPnPControlPoint &upnpControlPoint,
348	CUPnPLib &upnpLib,
349	IXML_Element *argument,
350	const std::string &WXUNUSED(SCPDURL))
351:
352m_UPnPControlPoint(upnpControlPoint),
353m_name                (upnpLib.Element_GetChildValueByTag(argument, "name")),
354m_direction           (upnpLib.Element_GetChildValueByTag(argument, "direction")),
355m_retval              (upnpLib.Element_GetFirstChildByTag(argument, "retval")),
356m_relatedStateVariable(upnpLib.Element_GetChildValueByTag(argument, "relatedStateVariable"))
357{
358	std::ostringstream msg;
359	msg <<	"\n    Argument:"                  <<
360		"\n        name: "                 << m_name <<
361		"\n        direction: "            << m_direction <<
362		"\n        retval: "               << m_retval <<
363		"\n        relatedStateVariable: " << m_relatedStateVariable;
364	AddDebugLogLineN(logUPnP, msg);
365}
366
367
368CUPnPAction::CUPnPAction(
369	const CUPnPControlPoint &upnpControlPoint,
370	CUPnPLib &upnpLib,
371	IXML_Element *action,
372	const std::string &SCPDURL)
373:
374m_UPnPControlPoint(upnpControlPoint),
375m_ArgumentList(upnpControlPoint, upnpLib, action, SCPDURL),
376m_name(upnpLib.Element_GetChildValueByTag(action, "name"))
377{
378	std::ostringstream msg;
379	msg <<	"\n    Action:"    <<
380		"\n        name: " << m_name;
381	AddDebugLogLineN(logUPnP, msg);
382}
383
384
385CUPnPAllowedValue::CUPnPAllowedValue(
386	const CUPnPControlPoint &upnpControlPoint,
387	CUPnPLib &upnpLib,
388	IXML_Element *allowedValue,
389	const std::string &WXUNUSED(SCPDURL))
390:
391m_UPnPControlPoint(upnpControlPoint),
392m_allowedValue(upnpLib.Element_GetTextValue(allowedValue))
393{
394	std::ostringstream msg;
395	msg <<	"\n    AllowedValue:"      <<
396		"\n        allowedValue: " << m_allowedValue;
397	AddDebugLogLineN(logUPnP, msg);
398}
399
400
401CUPnPStateVariable::CUPnPStateVariable(
402	const CUPnPControlPoint &upnpControlPoint,
403	CUPnPLib &upnpLib,
404	IXML_Element *stateVariable,
405	const std::string &SCPDURL)
406:
407m_UPnPControlPoint(upnpControlPoint),
408m_AllowedValueList(upnpControlPoint, upnpLib, stateVariable, SCPDURL),
409m_name        (upnpLib.Element_GetChildValueByTag(stateVariable, "name")),
410m_dataType    (upnpLib.Element_GetChildValueByTag(stateVariable, "dataType")),
411m_defaultValue(upnpLib.Element_GetChildValueByTag(stateVariable, "defaultValue")),
412m_sendEvents  (upnpLib.Element_GetAttributeByTag (stateVariable, "sendEvents"))
413{
414	std::ostringstream msg;
415	msg <<	"\n    StateVariable:"     <<
416		"\n        name: "         << m_name <<
417		"\n        dataType: "     << m_dataType <<
418		"\n        defaultValue: " << m_defaultValue <<
419		"\n        sendEvents: "   << m_sendEvents;
420	AddDebugLogLineN(logUPnP, msg);
421}
422
423
424CUPnPSCPD::CUPnPSCPD(
425	const CUPnPControlPoint &upnpControlPoint,
426	CUPnPLib &upnpLib,
427	IXML_Element *scpd,
428	const std::string &SCPDURL)
429:
430m_UPnPControlPoint(upnpControlPoint),
431m_ActionList(upnpControlPoint, upnpLib, scpd, SCPDURL),
432m_ServiceStateTable(upnpControlPoint, upnpLib, scpd, SCPDURL),
433m_SCPDURL(SCPDURL)
434{
435}
436
437
438CUPnPArgumentValue::CUPnPArgumentValue()
439:
440m_argument(),
441m_value()
442{
443}
444
445
446CUPnPArgumentValue::CUPnPArgumentValue(
447	const std::string &argument, const std::string &value)
448:
449m_argument(argument),
450m_value(value)
451{
452}
453
454
455CUPnPService::CUPnPService(
456	const CUPnPControlPoint &upnpControlPoint,
457	CUPnPLib &upnpLib,
458	IXML_Element *service,
459	const std::string &URLBase)
460:
461m_UPnPControlPoint(upnpControlPoint),
462m_upnpLib(upnpLib),
463m_serviceType(upnpLib.Element_GetChildValueByTag(service, "serviceType")),
464m_serviceId  (upnpLib.Element_GetChildValueByTag(service, "serviceId")),
465m_SCPDURL    (upnpLib.Element_GetChildValueByTag(service, "SCPDURL")),
466m_controlURL (upnpLib.Element_GetChildValueByTag(service, "controlURL")),
467m_eventSubURL(upnpLib.Element_GetChildValueByTag(service, "eventSubURL")),
468m_timeout(1801),
469m_SCPD(NULL)
470{
471	std::ostringstream msg;
472	int errcode;
473
474	std::vector<char> vscpdURL(URLBase.length() + m_SCPDURL.length() + 1);
475	char *scpdURL = &vscpdURL[0];
476	errcode = UpnpResolveURL(
477		URLBase.c_str(),
478		m_SCPDURL.c_str(),
479		scpdURL);
480	if( errcode != UPNP_E_SUCCESS ) {
481		msg << "Error generating scpdURL from " <<
482			"|" << URLBase << "|" <<
483			m_SCPDURL << "|.";
484		AddDebugLogLineN(logUPnP, msg);
485	} else {
486		m_absSCPDURL = scpdURL;
487	}
488
489	std::vector<char> vcontrolURL(
490		URLBase.length() + m_controlURL.length() + 1);
491	char *controlURL = &vcontrolURL[0];
492	errcode = UpnpResolveURL(
493		URLBase.c_str(),
494		m_controlURL.c_str(),
495		controlURL);
496	if( errcode != UPNP_E_SUCCESS ) {
497		msg << "Error generating controlURL from " <<
498			"|" << URLBase << "|" <<
499			m_controlURL << "|.";
500		AddDebugLogLineN(logUPnP, msg);
501	} else {
502		m_absControlURL = controlURL;
503	}
504
505	std::vector<char> veventURL(
506		URLBase.length() + m_eventSubURL.length() + 1);
507	char *eventURL = &veventURL[0];
508	errcode = UpnpResolveURL(
509		URLBase.c_str(),
510		m_eventSubURL.c_str(),
511		eventURL);
512	if( errcode != UPNP_E_SUCCESS ) {
513		msg << "Error generating eventURL from " <<
514			"|" << URLBase << "|" <<
515			m_eventSubURL << "|.";
516		AddDebugLogLineN(logUPnP, msg);
517	} else {
518		m_absEventSubURL = eventURL;
519	}
520
521	msg <<	"\n    Service:"             <<
522		"\n        serviceType: "    << m_serviceType <<
523		"\n        serviceId: "      << m_serviceId <<
524		"\n        SCPDURL: "        << m_SCPDURL <<
525		"\n        absSCPDURL: "     << m_absSCPDURL <<
526		"\n        controlURL: "     << m_controlURL <<
527		"\n        absControlURL: "  << m_absControlURL <<
528		"\n        eventSubURL: "    << m_eventSubURL <<
529		"\n        absEventSubURL: " << m_absEventSubURL;
530	AddDebugLogLineN(logUPnP, msg);
531
532	if (m_serviceType == upnpLib.UPNP_SERVICE_WAN_IP_CONNECTION ||
533	    m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION) {
534#if 0
535	    m_serviceType == upnpLib.UPNP_SERVICE_WAN_PPP_CONNECTION ||
536	    m_serviceType == upnpLib.UPNP_SERVICE_WAN_COMMON_INTERFACE_CONFIG ||
537	    m_serviceType == upnpLib.UPNP_SERVICE_LAYER3_FORWARDING) {
538#endif
539#if 0
540//#warning Delete this code on release.
541		if (!upnpLib.m_ctrlPoint.WanServiceDetected()) {
542			// This condition can be used to suspend the parse
543			// of the XML tree.
544#endif
545//#warning Delete this code when m_WanService is no longer used.
546			upnpLib.m_ctrlPoint.SetWanService(this);
547			// Log it
548			msg.str("");
549			msg << "WAN Service Detected: '" <<
550				m_serviceType << "'.";
551			AddDebugLogLineC(logUPnP, msg);
552			// Subscribe
553			upnpLib.m_ctrlPoint.Subscribe(*this);
554#if 0
555//#warning Delete this code on release.
556		} else {
557			msg.str("");
558			msg << "WAN service detected again: '" <<
559				m_serviceType <<
560				"'. Will only use the first instance.";
561			AddDebugLogLineC(logUPnP, msg);
562		}
563#endif
564	} else {
565		msg.str("");
566		msg << "Uninteresting service detected: '" <<
567			m_serviceType << "'. Ignoring.";
568		AddDebugLogLineC(logUPnP, msg);
569	}
570}
571
572
573CUPnPService::~CUPnPService()
574{
575}
576
577
578bool CUPnPService::Execute(
579	const std::string &ActionName,
580	const std::vector<CUPnPArgumentValue> &ArgValue) const
581{
582	std::ostringstream msg;
583	if (m_SCPD.get() == NULL) {
584		msg << "Service without SCPD Document, cannot execute action '" << ActionName <<
585			"' for service '" << GetServiceType() << "'.";
586		AddDebugLogLineN(logUPnP, msg);
587		return false;
588	}
589	std::ostringstream msgAction("Sending action ");
590	// Check for correct action name
591	ActionList::const_iterator itAction =
592		m_SCPD->GetActionList().find(ActionName);
593	if (itAction == m_SCPD->GetActionList().end()) {
594		msg << "Invalid action name '" << ActionName <<
595			"' for service '" << GetServiceType() << "'.";
596		AddDebugLogLineN(logUPnP, msg);
597		return false;
598	}
599	msgAction << ActionName << "(";
600	bool firstTime = true;
601	// Check for correct Argument/Value pairs
602	const CUPnPAction &action = *(itAction->second);
603	for (unsigned int i = 0; i < ArgValue.size(); ++i) {
604		ArgumentList::const_iterator itArg =
605			action.GetArgumentList().find(ArgValue[i].GetArgument());
606		if (itArg == action.GetArgumentList().end()) {
607			msg << "Invalid argument name '" << ArgValue[i].GetArgument() <<
608				"' for action '" << action.GetName() <<
609				"' for service '" << GetServiceType() << "'.";
610			AddDebugLogLineN(logUPnP, msg);
611			return false;
612		}
613		const CUPnPArgument &argument = *(itArg->second);
614		if (tolower(argument.GetDirection()[0]) != 'i' ||
615		    tolower(argument.GetDirection()[1]) != 'n') {
616			msg << "Invalid direction for argument '" <<
617				ArgValue[i].GetArgument() <<
618				"' for action '" << action.GetName() <<
619				"' for service '" << GetServiceType() << "'.";
620			AddDebugLogLineN(logUPnP, msg);
621			return false;
622		}
623		const std::string relatedStateVariableName =
624			argument.GetRelatedStateVariable();
625		if (!relatedStateVariableName.empty()) {
626			ServiceStateTable::const_iterator itSVT =
627				m_SCPD->GetServiceStateTable().
628				find(relatedStateVariableName);
629			if (itSVT == m_SCPD->GetServiceStateTable().end()) {
630				msg << "Inconsistent Service State Table, did not find '" <<
631					relatedStateVariableName <<
632					"' for argument '" << argument.GetName() <<
633					"' for action '" << action.GetName() <<
634					"' for service '" << GetServiceType() << "'.";
635				AddDebugLogLineN(logUPnP, msg);
636				return false;
637			}
638			const CUPnPStateVariable &stateVariable = *(itSVT->second);
639			if (	!stateVariable.GetAllowedValueList().empty() &&
640				stateVariable.GetAllowedValueList().find(ArgValue[i].GetValue()) ==
641					stateVariable.GetAllowedValueList().end()) {
642				msg << "Value not allowed '" << ArgValue[i].GetValue() <<
643					"' for state variable '" << relatedStateVariableName <<
644					"' for argument '" << argument.GetName() <<
645					"' for action '" << action.GetName() <<
646					"' for service '" << GetServiceType() << "'.";
647				AddDebugLogLineN(logUPnP, msg);
648				return false;
649			}
650		}
651		if (firstTime) {
652			firstTime = false;
653		} else {
654			msgAction << ", ";
655		}
656		msgAction <<
657			ArgValue[i].GetArgument() <<
658			"='" <<
659			ArgValue[i].GetValue() <<
660			"'";
661	}
662	msgAction << ")";
663	AddDebugLogLineN(logUPnP, msgAction);
664	// Everything is ok, make the action
665	IXML_Document *ActionDoc = NULL;
666	if (ArgValue.size()) {
667		for (unsigned int i = 0; i < ArgValue.size(); ++i) {
668			int ret = UpnpAddToAction(
669				&ActionDoc,
670				action.GetName().c_str(),
671				GetServiceType().c_str(),
672				ArgValue[i].GetArgument().c_str(),
673				ArgValue[i].GetValue().c_str());
674			if (ret != UPNP_E_SUCCESS) {
675				m_upnpLib.processUPnPErrorMessage(
676					"UpnpAddToAction", ret, NULL, NULL);
677				return false;
678			}
679		}
680	} else {
681		ActionDoc = UpnpMakeAction(
682			action.GetName().c_str(),
683			GetServiceType().c_str(),
684			0, NULL);
685		if (!ActionDoc) {
686			msg << "Error: UpnpMakeAction returned NULL.";
687			AddLogLineU(false, logUPnP, msg);
688			return false;
689		}
690	}
691#if 0
692	// Send the action asynchronously
693	UpnpSendActionAsync(
694		m_UPnPControlPoint.GetUPnPClientHandle(),
695		GetAbsControlURL().c_str(),
696		GetServiceType().c_str(),
697		NULL, ActionDoc,
698		static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
699		NULL);
700	return true;
701#endif
702
703	// Send the action synchronously
704	IXML_Document *RespDoc = NULL;
705	int ret = UpnpSendAction(
706		m_UPnPControlPoint.GetUPnPClientHandle(),
707		GetAbsControlURL().c_str(),
708		GetServiceType().c_str(),
709		NULL, ActionDoc, &RespDoc);
710	if (ret != UPNP_E_SUCCESS) {
711		m_upnpLib.processUPnPErrorMessage(
712			"UpnpSendAction", ret, NULL, RespDoc);
713		ixmlDocument_free(ActionDoc);
714		ixmlDocument_free(RespDoc);
715		return false;
716	}
717	ixmlDocument_free(ActionDoc);
718
719	// Check the response document
720	m_upnpLib.ProcessActionResponse(
721		RespDoc, action.GetName());
722
723	// Free the response document
724	ixmlDocument_free(RespDoc);
725
726	return true;
727}
728
729
730const std::string CUPnPService::GetStateVariable(
731	const std::string &stateVariableName) const
732{
733	std::ostringstream msg;
734	DOMString StVarVal;
735	int ret = UpnpGetServiceVarStatus(
736		m_UPnPControlPoint.GetUPnPClientHandle(),
737		GetAbsControlURL().c_str(),
738		stateVariableName.c_str(),
739		&StVarVal);
740	if (ret != UPNP_E_SUCCESS) {
741		msg << "GetStateVariable(\"" <<
742			stateVariableName <<
743			"\"): in a call to UpnpGetServiceVarStatus";
744		m_upnpLib.processUPnPErrorMessage(
745			msg.str(), ret, StVarVal, NULL);
746		return stdEmptyString;
747	}
748	msg << "GetStateVariable: " <<
749		stateVariableName <<
750		"='" <<
751		StVarVal <<
752		"'.";
753	AddDebugLogLineN(logUPnP, msg);
754	return stdEmptyString;
755}
756
757
758CUPnPDevice::CUPnPDevice(
759	const CUPnPControlPoint &upnpControlPoint,
760	CUPnPLib &upnpLib,
761	IXML_Element *device,
762	const std::string &URLBase)
763:
764m_UPnPControlPoint(upnpControlPoint),
765m_DeviceList(upnpControlPoint, upnpLib, device, URLBase),
766m_ServiceList(upnpControlPoint, upnpLib, device, URLBase),
767m_deviceType       (upnpLib.Element_GetChildValueByTag(device, "deviceType")),
768m_friendlyName     (upnpLib.Element_GetChildValueByTag(device, "friendlyName")),
769m_manufacturer     (upnpLib.Element_GetChildValueByTag(device, "manufacturer")),
770m_manufacturerURL  (upnpLib.Element_GetChildValueByTag(device, "manufacturerURL")),
771m_modelDescription (upnpLib.Element_GetChildValueByTag(device, "modelDescription")),
772m_modelName        (upnpLib.Element_GetChildValueByTag(device, "modelName")),
773m_modelNumber      (upnpLib.Element_GetChildValueByTag(device, "modelNumber")),
774m_modelURL         (upnpLib.Element_GetChildValueByTag(device, "modelURL")),
775m_serialNumber     (upnpLib.Element_GetChildValueByTag(device, "serialNumber")),
776m_UDN              (upnpLib.Element_GetChildValueByTag(device, "UDN")),
777m_UPC              (upnpLib.Element_GetChildValueByTag(device, "UPC")),
778m_presentationURL  (upnpLib.Element_GetChildValueByTag(device, "presentationURL"))
779{
780	std::ostringstream msg;
781	int presURLlen = strlen(URLBase.c_str()) +
782		strlen(m_presentationURL.c_str()) + 2;
783	std::vector<char> vpresURL(presURLlen);
784	char* presURL = &vpresURL[0];
785	int errcode = UpnpResolveURL(
786		URLBase.c_str(),
787		m_presentationURL.c_str(),
788		presURL);
789	if (errcode != UPNP_E_SUCCESS) {
790		msg << "Error generating presentationURL from " <<
791			"|" << URLBase << "|" <<
792			m_presentationURL << "|.";
793		AddDebugLogLineN(logUPnP, msg);
794	} else {
795		m_presentationURL = presURL;
796	}
797
798	msg.str("");
799	msg <<	"\n    Device: "                <<
800		"\n        friendlyName: "      << m_friendlyName <<
801		"\n        deviceType: "        << m_deviceType <<
802		"\n        manufacturer: "      << m_manufacturer <<
803		"\n        manufacturerURL: "   << m_manufacturerURL <<
804		"\n        modelDescription: "  << m_modelDescription <<
805		"\n        modelName: "         << m_modelName <<
806		"\n        modelNumber: "       << m_modelNumber <<
807		"\n        modelURL: "          << m_modelURL <<
808		"\n        serialNumber: "      << m_serialNumber <<
809		"\n        UDN: "               << m_UDN <<
810		"\n        UPC: "               << m_UPC <<
811		"\n        presentationURL: "   << m_presentationURL;
812	AddDebugLogLineN(logUPnP, msg);
813}
814
815
816CUPnPRootDevice::CUPnPRootDevice(
817	const CUPnPControlPoint &upnpControlPoint,
818	CUPnPLib &upnpLib,
819	IXML_Element *rootDevice,
820	const std::string &OriginalURLBase,
821	const std::string &FixedURLBase,
822	const char *location,
823	int expires)
824:
825CUPnPDevice(upnpControlPoint, upnpLib, rootDevice, FixedURLBase),
826m_UPnPControlPoint(upnpControlPoint),
827m_URLBase(OriginalURLBase),
828m_location(location),
829m_expires(expires)
830{
831	std::ostringstream msg;
832	msg <<
833		"\n    Root Device: "       <<
834		"\n        URLBase: "       << m_URLBase <<
835		"\n        Fixed URLBase: " << FixedURLBase <<
836		"\n        location: "      << m_location <<
837		"\n        expires: "       << m_expires;
838	AddDebugLogLineN(logUPnP, msg);
839}
840
841
842CUPnPControlPoint *CUPnPControlPoint::s_CtrlPoint = NULL;
843
844
845CUPnPControlPoint::CUPnPControlPoint(unsigned short udpPort)
846:
847m_upnpLib(*this),
848m_UPnPClientHandle(),
849m_RootDeviceMap(),
850m_ServiceMap(),
851m_ActivePortMappingsMap(),
852m_RootDeviceListMutex(),
853m_IGWDeviceDetected(false),
854m_WanService(NULL)
855{
856	// Pointer to self
857	s_CtrlPoint = this;
858	// Null string at first
859	std::ostringstream msg;
860
861	// Start UPnP
862	int ret;
863	char *ipAddress = NULL;
864	unsigned short port = 0;
865	ret = UpnpInit(ipAddress, udpPort);
866	if (ret != UPNP_E_SUCCESS) {
867		msg << "error(UpnpInit): Error code ";
868		goto error;
869	}
870	port = UpnpGetServerPort();
871	ipAddress = UpnpGetServerIpAddress();
872	msg << "bound to " << ipAddress << ":" <<
873		port << ".";
874	AddLogLineU(false, logUPnP, msg);
875	msg.str("");
876	ret = UpnpRegisterClient(
877		static_cast<Upnp_FunPtr>(&CUPnPControlPoint::Callback),
878		&m_UPnPClientHandle,
879		&m_UPnPClientHandle);
880	if (ret != UPNP_E_SUCCESS) {
881		msg << "error(UpnpRegisterClient): Error registering callback: ";
882		goto error;
883	}
884
885	// We could ask for just the right device here. If the root device
886	// contains the device we want, it will respond with the full XML doc,
887	// including the root device and every sub-device it has.
888	//
889	// But lets find out what we have in our network by calling UPNP_ROOT_DEVICE.
890	//
891	// We should not search twice, because this will produce two
892	// UPNP_DISCOVERY_SEARCH_TIMEOUT events, and we might end with problems
893	// on the mutex.
894	ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_ROOT_DEVICE.c_str(), NULL);
895	//ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_IGW.c_str(), this);
896	//ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_LAN.c_str(), this);
897	//ret = UpnpSearchAsync(m_UPnPClientHandle, 3, m_upnpLib.UPNP_DEVICE_WAN_CONNECTION.c_str(), this);
898	if (ret != UPNP_E_SUCCESS) {
899		msg << "error(UpnpSearchAsync): Error sending search request: ";
900		goto error;
901	}
902
903	// Wait for the UPnP initialization to complete.
904	{
905		// Lock the search timeout mutex
906		m_WaitForSearchTimeoutMutex.Lock();
907
908		// Lock it again, so that we block. Unlocking will only happen
909		// when the UPNP_DISCOVERY_SEARCH_TIMEOUT event occurs at the
910		// callback.
911		CUPnPMutexLocker lock(m_WaitForSearchTimeoutMutex);
912	}
913	return;
914
915	// Error processing
916error:
917	UpnpFinish();
918	msg << ret << ": " << m_upnpLib.GetUPnPErrorMessage(ret) << ".";
919	throw CUPnPException(msg);
920}
921
922
923CUPnPControlPoint::~CUPnPControlPoint()
924{
925	for(	RootDeviceMap::iterator it = m_RootDeviceMap.begin();
926		it != m_RootDeviceMap.end();
927		++it) {
928		delete it->second;
929	}
930	// Remove all first
931	// RemoveAll();
932	UpnpUnRegisterClient(m_UPnPClientHandle);
933	UpnpFinish();
934}
935
936
937bool CUPnPControlPoint::AddPortMappings(
938	std::vector<CUPnPPortMapping> &upnpPortMapping)
939{
940	std::ostringstream msg;
941	if (!WanServiceDetected()) {
942		msg <<  "UPnP Error: "
943			"CUPnPControlPoint::AddPortMapping: "
944			"WAN Service not detected.";
945		AddLogLineU(true, logUPnP, msg);
946		return false;
947	}
948
949	int n = upnpPortMapping.size();
950	bool ok = false;
951
952	// Check the number of port mappings before
953	std::istringstream PortMappingNumberOfEntries(
954		m_WanService->GetStateVariable(
955			"PortMappingNumberOfEntries"));
956	unsigned long oldNumberOfEntries;
957	PortMappingNumberOfEntries >> oldNumberOfEntries;
958
959	// Add the enabled port mappings
960	for (int i = 0; i < n; ++i) {
961		if (upnpPortMapping[i].getEnabled() == "1") {
962			// Add the mapping to the control point
963			// active mappings list
964			m_ActivePortMappingsMap[upnpPortMapping[i].getKey()] =
965				upnpPortMapping[i];
966
967			// Add the port mapping
968			PrivateAddPortMapping(upnpPortMapping[i]);
969		}
970	}
971
972	// Test some variables, this is deprecated, might not work
973	// with some routers
974	m_WanService->GetStateVariable("ConnectionType");
975	m_WanService->GetStateVariable("PossibleConnectionTypes");
976	m_WanService->GetStateVariable("ConnectionStatus");
977	m_WanService->GetStateVariable("Uptime");
978	m_WanService->GetStateVariable("LastConnectionError");
979	m_WanService->GetStateVariable("RSIPAvailable");
980	m_WanService->GetStateVariable("NATEnabled");
981	m_WanService->GetStateVariable("ExternalIPAddress");
982	m_WanService->GetStateVariable("PortMappingNumberOfEntries");
983	m_WanService->GetStateVariable("PortMappingLeaseDuration");
984
985	// Just for testing
986	std::vector<CUPnPArgumentValue> argval;
987	argval.resize(0);
988	m_WanService->Execute("GetStatusInfo", argval);
989
990#if 0
991	// These do not work. Their value must be requested for a
992	// specific port mapping.
993	m_WanService->GetStateVariable("PortMappingEnabled");
994	m_WanService->GetStateVariable("RemoteHost");
995	m_WanService->GetStateVariable("ExternalPort");
996	m_WanService->GetStateVariable("InternalPort");
997	m_WanService->GetStateVariable("PortMappingProtocol");
998	m_WanService->GetStateVariable("InternalClient");
999	m_WanService->GetStateVariable("PortMappingDescription");
1000#endif
1001
1002	// Debug only
1003	msg.str("");
1004	msg << "CUPnPControlPoint::DeletePortMappings: "
1005		"m_ActivePortMappingsMap.size() == " <<
1006		m_ActivePortMappingsMap.size();
1007	AddDebugLogLineN(logUPnP, msg);
1008
1009	// Not very good, must find a better test
1010	PortMappingNumberOfEntries.str(
1011		m_WanService->GetStateVariable(
1012			"PortMappingNumberOfEntries"));
1013	unsigned long newNumberOfEntries;
1014	PortMappingNumberOfEntries >> newNumberOfEntries;
1015	ok = newNumberOfEntries - oldNumberOfEntries == 4;
1016
1017	return ok;
1018}
1019
1020
1021void CUPnPControlPoint::RefreshPortMappings()
1022{
1023	for (	PortMappingMap::iterator it = m_ActivePortMappingsMap.begin();
1024		it != m_ActivePortMappingsMap.end();
1025		++it) {
1026		PrivateAddPortMapping(it->second);
1027	}
1028
1029	// For testing
1030	m_WanService->GetStateVariable("PortMappingNumberOfEntries");
1031}
1032
1033
1034bool CUPnPControlPoint::PrivateAddPortMapping(
1035	CUPnPPortMapping &upnpPortMapping)
1036{
1037	// Get an IP address. The UPnP server one must do.
1038	std::string ipAddress(UpnpGetServerIpAddress());
1039
1040	// Start building the action
1041	std::string actionName("AddPortMapping");
1042	std::vector<CUPnPArgumentValue> argval(8);
1043
1044	// Action parameters
1045	argval[0].SetArgument("NewRemoteHost");
1046	argval[0].SetValue("");
1047	argval[1].SetArgument("NewExternalPort");
1048	argval[1].SetValue(upnpPortMapping.getPort());
1049	argval[2].SetArgument("NewProtocol");
1050	argval[2].SetValue(upnpPortMapping.getProtocol());
1051	argval[3].SetArgument("NewInternalPort");
1052	argval[3].SetValue(upnpPortMapping.getPort());
1053	argval[4].SetArgument("NewInternalClient");
1054	argval[4].SetValue(ipAddress);
1055	argval[5].SetArgument("NewEnabled");
1056	argval[5].SetValue("1");
1057	argval[6].SetArgument("NewPortMappingDescription");
1058	argval[6].SetValue(upnpPortMapping.getDescription());
1059	argval[7].SetArgument("NewLeaseDuration");
1060	argval[7].SetValue("0");
1061
1062	// Execute
1063	bool ret = true;
1064	for (ServiceMap::iterator it = m_ServiceMap.begin();
1065	     it != m_ServiceMap.end(); ++it) {
1066		ret &= it->second->Execute(actionName, argval);
1067	}
1068
1069	return ret;
1070}
1071
1072
1073bool CUPnPControlPoint::DeletePortMappings(
1074	std::vector<CUPnPPortMapping> &upnpPortMapping)
1075{
1076	std::ostringstream msg;
1077	if (!WanServiceDetected()) {
1078		msg <<  "UPnP Error: "
1079			"CUPnPControlPoint::DeletePortMapping: "
1080			"WAN Service not detected.";
1081		AddLogLineU(true, logUPnP, msg);
1082		return false;
1083	}
1084
1085	int n = upnpPortMapping.size();
1086	bool ok = false;
1087
1088	// Check the number of port mappings before
1089	std::istringstream PortMappingNumberOfEntries(
1090		m_WanService->GetStateVariable(
1091			"PortMappingNumberOfEntries"));
1092	unsigned long oldNumberOfEntries;
1093	PortMappingNumberOfEntries >> oldNumberOfEntries;
1094
1095	// Delete the enabled port mappings
1096	for (int i = 0; i < n; ++i) {
1097		if (upnpPortMapping[i].getEnabled() == "1") {
1098			// Delete the mapping from the control point
1099			// active mappings list
1100			PortMappingMap::iterator it =
1101				m_ActivePortMappingsMap.find(
1102					upnpPortMapping[i].getKey());
1103			if (it != m_ActivePortMappingsMap.end()) {
1104				m_ActivePortMappingsMap.erase(it);
1105			} else {
1106				msg <<  "UPnP Error: "
1107					"CUPnPControlPoint::DeletePortMapping: "
1108					"Mapping was not found in the active "
1109					"mapping map.";
1110				AddLogLineU(true, logUPnP, msg);
1111			}
1112
1113			// Delete the port mapping
1114			PrivateDeletePortMapping(upnpPortMapping[i]);
1115		}
1116	}
1117
1118	// Debug only
1119	msg.str("");
1120	msg << "CUPnPControlPoint::DeletePortMappings: "
1121		"m_ActivePortMappingsMap.size() == " <<
1122		m_ActivePortMappingsMap.size();
1123	AddDebugLogLineN(logUPnP, msg);
1124
1125	// Not very good, must find a better test
1126	PortMappingNumberOfEntries.str(
1127		m_WanService->GetStateVariable(
1128			"PortMappingNumberOfEntries"));
1129	unsigned long newNumberOfEntries;
1130	PortMappingNumberOfEntries >> newNumberOfEntries;
1131	ok = oldNumberOfEntries - newNumberOfEntries == 4;
1132
1133	return ok;
1134}
1135
1136
1137bool CUPnPControlPoint::PrivateDeletePortMapping(
1138	CUPnPPortMapping &upnpPortMapping)
1139{
1140	// Start building the action
1141	std::string actionName("DeletePortMapping");
1142	std::vector<CUPnPArgumentValue> argval(3);
1143
1144	// Action parameters
1145	argval[0].SetArgument("NewRemoteHost");
1146	argval[0].SetValue("");
1147	argval[1].SetArgument("NewExternalPort");
1148	argval[1].SetValue(upnpPortMapping.getPort());
1149	argval[2].SetArgument("NewProtocol");
1150	argval[2].SetValue(upnpPortMapping.getProtocol());
1151
1152	// Execute
1153	bool ret = true;
1154	for (ServiceMap::iterator it = m_ServiceMap.begin();
1155	     it != m_ServiceMap.end(); ++it) {
1156		ret &= it->second->Execute(actionName, argval);
1157	}
1158
1159	return ret;
1160}
1161
1162
1163// This function is static
1164int CUPnPControlPoint::Callback(Upnp_EventType EventType, void *Event, void * /*Cookie*/)
1165{
1166	std::ostringstream msg;
1167	std::ostringstream msg2;
1168	// Somehow, this is unreliable. UPNP_DISCOVERY_ADVERTISEMENT_ALIVE events
1169	// happen with a wrong cookie and... boom!
1170	// CUPnPControlPoint *upnpCP = static_cast<CUPnPControlPoint *>(Cookie);
1171	CUPnPControlPoint *upnpCP = CUPnPControlPoint::s_CtrlPoint;
1172
1173	//fprintf(stderr, "Callback: %d, Cookie: %p\n", EventType, Cookie);
1174	switch (EventType) {
1175	case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
1176		//fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n");
1177		msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_ALIVE): ";
1178		msg2<< "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: ";
1179		goto upnpDiscovery;
1180	case UPNP_DISCOVERY_SEARCH_RESULT: {
1181		//fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_RESULT\n");
1182		msg << "error(UPNP_DISCOVERY_SEARCH_RESULT): ";
1183		msg2<< "UPNP_DISCOVERY_SEARCH_RESULT: ";
1184		// UPnP Discovery
1185upnpDiscovery:
1186		struct Upnp_Discovery *d_event = (struct Upnp_Discovery *)Event;
1187		IXML_Document *doc = NULL;
1188		int ret;
1189		if (d_event->ErrCode != UPNP_E_SUCCESS) {
1190			msg << upnpCP->m_upnpLib.GetUPnPErrorMessage(d_event->ErrCode) << ".";
1191			AddDebugLogLineC(logUPnP, msg);
1192		}
1193		// Get the XML tree device description in doc
1194		ret = UpnpDownloadXmlDoc(d_event->Location, &doc);
1195		if (ret != UPNP_E_SUCCESS) {
1196			msg << "Error retrieving device description from " <<
1197				d_event->Location << ": " <<
1198				upnpCP->m_upnpLib.GetUPnPErrorMessage(ret) <<
1199				"(" << ret << ").";
1200			AddDebugLogLineC(logUPnP, msg);
1201		} else {
1202			msg2 << "Retrieving device description from " <<
1203				d_event->Location << ".";
1204			AddDebugLogLineN(logUPnP, msg2);
1205		}
1206		if (doc) {
1207			// Get the root node
1208			IXML_Element *root =
1209				upnpCP->m_upnpLib.Element_GetRootElement(doc);
1210			// Extract the URLBase
1211			const std::string urlBase = upnpCP->m_upnpLib.
1212				Element_GetChildValueByTag(root, "URLBase");
1213			// Get the root device
1214			IXML_Element *rootDevice = upnpCP->m_upnpLib.
1215				Element_GetFirstChildByTag(root, "device");
1216			// Extract the deviceType
1217			std::string devType(upnpCP->m_upnpLib.
1218				Element_GetChildValueByTag(rootDevice, "deviceType"));
1219			// Only add device if it is an InternetGatewayDevice
1220			if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1221				// This condition can be used to auto-detect
1222				// the UPnP device we are interested in.
1223				// Obs.: Don't block the entry here on this
1224				// condition! There may be more than one device,
1225				// and the first that enters may not be the one
1226				// we are interested in!
1227				upnpCP->SetIGWDeviceDetected(true);
1228				// Log it if not UPNP_DISCOVERY_ADVERTISEMENT_ALIVE,
1229				// we don't want to spam our logs.
1230				if (EventType != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) {
1231					msg.str("Internet Gateway Device Detected.");
1232					AddLogLineU(true, logUPnP, msg);
1233				}
1234				// Add the root device to our list
1235				upnpCP->AddRootDevice(rootDevice, urlBase,
1236					d_event->Location, d_event->Expires);
1237			}
1238			// Free the XML doc tree
1239			ixmlDocument_free(doc);
1240		}
1241		break;
1242	}
1243	case UPNP_DISCOVERY_SEARCH_TIMEOUT: {
1244		//fprintf(stderr, "Callback: UPNP_DISCOVERY_SEARCH_TIMEOUT\n");
1245		// Search timeout
1246		msg << "UPNP_DISCOVERY_SEARCH_TIMEOUT.";
1247		AddDebugLogLineN(logUPnP, msg);
1248
1249		// Unlock the search timeout mutex
1250		upnpCP->m_WaitForSearchTimeoutMutex.Unlock();
1251
1252		break;
1253	}
1254	case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: {
1255		//fprintf(stderr, "Callback: UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n");
1256		// UPnP Device Removed
1257		struct Upnp_Discovery *dab_event = (struct Upnp_Discovery *)Event;
1258		if (dab_event->ErrCode != UPNP_E_SUCCESS) {
1259			msg << "error(UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE): " <<
1260				upnpCP->m_upnpLib.GetUPnPErrorMessage(dab_event->ErrCode) <<
1261				".";
1262			AddDebugLogLineC(logUPnP, msg);
1263		}
1264		std::string devType = dab_event->DeviceType;
1265		// Check for an InternetGatewayDevice and removes it from the list
1266		std::transform(devType.begin(), devType.end(), devType.begin(), tolower);
1267		if (stdStringIsEqualCI(devType, upnpCP->m_upnpLib.UPNP_DEVICE_IGW)) {
1268			upnpCP->RemoveRootDevice(dab_event->DeviceId);
1269		}
1270		break;
1271	}
1272	case UPNP_EVENT_RECEIVED: {
1273		//fprintf(stderr, "Callback: UPNP_EVENT_RECEIVED\n");
1274		// Event reveived
1275		struct Upnp_Event *e_event = (struct Upnp_Event *)Event;
1276		const std::string Sid = e_event->Sid;
1277		// Parses the event
1278		upnpCP->OnEventReceived(Sid, e_event->EventKey, e_event->ChangedVariables);
1279		break;
1280	}
1281	case UPNP_EVENT_SUBSCRIBE_COMPLETE:
1282		//fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIBE_COMPLETE\n");
1283		msg << "error(UPNP_EVENT_SUBSCRIBE_COMPLETE): ";
1284		goto upnpEventRenewalComplete;
1285	case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
1286		//fprintf(stderr, "Callback: UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n");
1287		msg << "error(UPNP_EVENT_UNSUBSCRIBE_COMPLETE): ";
1288		goto upnpEventRenewalComplete;
1289	case UPNP_EVENT_RENEWAL_COMPLETE: {
1290		//fprintf(stderr, "Callback: UPNP_EVENT_RENEWAL_COMPLETE\n");
1291		msg << "error(UPNP_EVENT_RENEWAL_COMPLETE): ";
1292upnpEventRenewalComplete:
1293		struct Upnp_Event_Subscribe *es_event =
1294			(struct Upnp_Event_Subscribe *)Event;
1295		if (es_event->ErrCode != UPNP_E_SUCCESS) {
1296			msg << "Error in Event Subscribe Callback";
1297			upnpCP->m_upnpLib.processUPnPErrorMessage(
1298				msg.str(), es_event->ErrCode, NULL, NULL);
1299		} else {
1300#if 0
1301			TvCtrlPointHandleSubscribeUpdate(
1302				es_event->PublisherUrl,
1303				es_event->Sid,
1304				es_event->TimeOut );
1305#endif
1306		}
1307
1308		break;
1309	}
1310
1311	case UPNP_EVENT_AUTORENEWAL_FAILED:
1312		//fprintf(stderr, "Callback: UPNP_EVENT_AUTORENEWAL_FAILED\n");
1313		msg << "error(UPNP_EVENT_AUTORENEWAL_FAILED): ";
1314		msg2 << "UPNP_EVENT_AUTORENEWAL_FAILED: ";
1315		goto upnpEventSubscriptionExpired;
1316	case UPNP_EVENT_SUBSCRIPTION_EXPIRED: {
1317		//fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_EXPIRED\n");
1318		msg << "error(UPNP_EVENT_SUBSCRIPTION_EXPIRED): ";
1319		msg2 << "UPNP_EVENT_SUBSCRIPTION_EXPIRED: ";
1320upnpEventSubscriptionExpired:
1321		struct Upnp_Event_Subscribe *es_event =
1322			(struct Upnp_Event_Subscribe *)Event;
1323		Upnp_SID newSID;
1324		int TimeOut = 1801;
1325		int ret = UpnpSubscribe(
1326			upnpCP->m_UPnPClientHandle,
1327			es_event->PublisherUrl,
1328			&TimeOut,
1329			newSID);
1330		if (ret != UPNP_E_SUCCESS) {
1331			msg << "Error Subscribing to EventURL";
1332			upnpCP->m_upnpLib.processUPnPErrorMessage(
1333				msg.str(), es_event->ErrCode, NULL, NULL);
1334		} else {
1335			ServiceMap::iterator it =
1336				upnpCP->m_ServiceMap.find(es_event->PublisherUrl);
1337			if (it != upnpCP->m_ServiceMap.end()) {
1338				CUPnPService &service = *(it->second);
1339				service.SetTimeout(TimeOut);
1340				service.SetSID(newSID);
1341				msg2 << "Re-subscribed to EventURL '" <<
1342					es_event->PublisherUrl <<
1343					"' with SID == '" <<
1344					newSID << "'.";
1345				AddDebugLogLineC(logUPnP, msg2);
1346				// In principle, we should test to see if the
1347				// service is the same. But here we only have one
1348				// service, so...
1349				upnpCP->RefreshPortMappings();
1350			} else {
1351				msg << "Error: did not find service " <<
1352					newSID << " in the service map.";
1353				AddDebugLogLineC(logUPnP, msg);
1354			}
1355		}
1356		break;
1357	}
1358	case UPNP_CONTROL_ACTION_COMPLETE: {
1359		//fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_COMPLETE\n");
1360		// This is here if we choose to do this asynchronously
1361		struct Upnp_Action_Complete *a_event =
1362			(struct Upnp_Action_Complete *)Event;
1363		if (a_event->ErrCode != UPNP_E_SUCCESS) {
1364			upnpCP->m_upnpLib.processUPnPErrorMessage(
1365				"UpnpSendActionAsync",
1366				a_event->ErrCode, NULL,
1367				a_event->ActionResult);
1368		} else {
1369			// Check the response document
1370			upnpCP->m_upnpLib.ProcessActionResponse(
1371				a_event->ActionResult,
1372				"<UpnpSendActionAsync>");
1373		}
1374		/* No need for any processing here, just print out results.
1375		 * Service state table updates are handled by events.
1376		 */
1377		break;
1378	}
1379	case UPNP_CONTROL_GET_VAR_COMPLETE: {
1380		//fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_COMPLETE\n");
1381		msg << "error(UPNP_CONTROL_GET_VAR_COMPLETE): ";
1382		struct Upnp_State_Var_Complete *sv_event =
1383			(struct Upnp_State_Var_Complete *)Event;
1384		if (sv_event->ErrCode != UPNP_E_SUCCESS) {
1385			msg << "m_UpnpGetServiceVarStatusAsync";
1386			upnpCP->m_upnpLib.processUPnPErrorMessage(
1387				msg.str(), sv_event->ErrCode, NULL, NULL);
1388		} else {
1389#if 0
1390			// Warning: The use of UpnpGetServiceVarStatus and
1391			// UpnpGetServiceVarStatusAsync is deprecated by the
1392			// UPnP forum.
1393			TvCtrlPointHandleGetVar(
1394				sv_event->CtrlUrl,
1395				sv_event->StateVarName,
1396				sv_event->CurrentVal );
1397#endif
1398		}
1399		break;
1400	}
1401	// ignore these cases, since this is not a device
1402	case UPNP_CONTROL_GET_VAR_REQUEST:
1403		//fprintf(stderr, "Callback: UPNP_CONTROL_GET_VAR_REQUEST\n");
1404		msg << "error(UPNP_CONTROL_GET_VAR_REQUEST): ";
1405		goto eventSubscriptionRequest;
1406	case UPNP_CONTROL_ACTION_REQUEST:
1407		//fprintf(stderr, "Callback: UPNP_CONTROL_ACTION_REQUEST\n");
1408		msg << "error(UPNP_CONTROL_ACTION_REQUEST): ";
1409		goto eventSubscriptionRequest;
1410	case UPNP_EVENT_SUBSCRIPTION_REQUEST:
1411		//fprintf(stderr, "Callback: UPNP_EVENT_SUBSCRIPTION_REQUEST\n");
1412		msg << "error(UPNP_EVENT_SUBSCRIPTION_REQUEST): ";
1413eventSubscriptionRequest:
1414		msg << "This is not a UPnP Device, this is a UPnP Control Point, event ignored.";
1415		AddDebugLogLineC(logUPnP, msg);
1416		break;
1417	default:
1418		// Humm, this is not good, we forgot to handle something...
1419		fprintf(stderr,
1420			"Callback: default... Unknown event:'%d', not good.\n",
1421			EventType);
1422		msg << "error(UPnP::Callback): Event not handled:'" <<
1423			EventType << "'.";
1424		fprintf(stderr, "%s\n", msg.str().c_str());
1425		AddDebugLogLineC(logUPnP, msg);
1426		// Better not throw in the callback. Who would catch it?
1427		//throw CUPnPException(msg);
1428		break;
1429	}
1430
1431	return 0;
1432}
1433
1434
1435void CUPnPControlPoint::OnEventReceived(
1436		const std::string &Sid,
1437		int EventKey,
1438		IXML_Document *ChangedVariablesDoc)
1439{
1440	std::ostringstream msg;
1441	msg << "UPNP_EVENT_RECEIVED:" <<
1442		"\n    SID: " << Sid <<
1443		"\n    Key: " << EventKey <<
1444		"\n    Property list:";
1445	IXML_Element *root =
1446		m_upnpLib.Element_GetRootElement(ChangedVariablesDoc);
1447	IXML_Element *child =
1448		m_upnpLib.Element_GetFirstChild(root);
1449	if (child) {
1450		while (child) {
1451			IXML_Element *child2 =
1452				m_upnpLib.Element_GetFirstChild(child);
1453			const DOMString childTag =
1454				m_upnpLib.Element_GetTag(child2);
1455			std::string childValue =
1456				m_upnpLib.Element_GetTextValue(child2);
1457			msg << "\n        " <<
1458				childTag << "='" <<
1459				childValue << "'";
1460			child = m_upnpLib.Element_GetNextSibling(child);
1461		}
1462	} else {
1463		msg << "\n    Empty property list.";
1464	}
1465	AddDebugLogLineC(logUPnP, msg);
1466	// Freeing that doc segfaults. Probably should not be freed.
1467	//ixmlDocument_free(ChangedVariablesDoc);
1468}
1469
1470
1471void CUPnPControlPoint::AddRootDevice(
1472	IXML_Element *rootDevice, const std::string &urlBase,
1473	const char *location, int expires)
1474{
1475	// Lock the Root Device List
1476	CUPnPMutexLocker lock(m_RootDeviceListMutex);
1477
1478	// Root node's URLBase
1479	std::string OriginalURLBase(urlBase);
1480	std::string FixedURLBase(OriginalURLBase.empty() ?
1481		location :
1482		OriginalURLBase);
1483
1484	// Get the UDN (Unique Device Name)
1485	std::string UDN(
1486		m_upnpLib.Element_GetChildValueByTag(rootDevice, "UDN"));
1487	RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1488	bool alreadyAdded = it != m_RootDeviceMap.end();
1489	if (alreadyAdded) {
1490		// Just set the expires field
1491		it->second->SetExpires(expires);
1492	} else {
1493		// Add a new root device to the root device list
1494		CUPnPRootDevice *upnpRootDevice = new CUPnPRootDevice(
1495			*this, m_upnpLib, rootDevice,
1496			OriginalURLBase, FixedURLBase,
1497			location, expires);
1498		m_RootDeviceMap[upnpRootDevice->GetUDN()] = upnpRootDevice;
1499	}
1500}
1501
1502
1503void CUPnPControlPoint::RemoveRootDevice(const char *udn)
1504{
1505	// Lock the Root Device List
1506	CUPnPMutexLocker lock(m_RootDeviceListMutex);
1507
1508	// Remove
1509	std::string UDN(udn);
1510	RootDeviceMap::iterator it = m_RootDeviceMap.find(UDN);
1511	if (it != m_RootDeviceMap.end()) {
1512		delete it->second;
1513		m_RootDeviceMap.erase(UDN);
1514	}
1515}
1516
1517
1518void CUPnPControlPoint::Subscribe(CUPnPService &service)
1519{
1520	std::ostringstream msg;
1521
1522	IXML_Document *scpdDoc = NULL;
1523	int errcode = UpnpDownloadXmlDoc(
1524		service.GetAbsSCPDURL().c_str(), &scpdDoc);
1525	if (errcode == UPNP_E_SUCCESS) {
1526		// Get the root node of this service (the SCPD Document)
1527		IXML_Element *scpdRoot =
1528			m_upnpLib.Element_GetRootElement(scpdDoc);
1529		CUPnPSCPD *scpd = new CUPnPSCPD(*this, m_upnpLib,
1530			scpdRoot, service.GetAbsSCPDURL());
1531		service.SetSCPD(scpd);
1532		m_ServiceMap[service.GetAbsEventSubURL()] = &service;
1533		msg << "Successfully retrieved SCPD Document for service " <<
1534			service.GetServiceType() << ", absEventSubURL: " <<
1535			service.GetAbsEventSubURL() << ".";
1536		AddLogLineU(true, logUPnP, msg);
1537		msg.str("");
1538
1539		// Now try to subscribe to this service. If the subscription
1540		// is not successfull, we will not be notified about events,
1541		// but it may be possible to use the service anyway.
1542		errcode = UpnpSubscribe(m_UPnPClientHandle,
1543			service.GetAbsEventSubURL().c_str(),
1544			service.GetTimeoutAddr(),
1545			service.GetSID());
1546		if (errcode == UPNP_E_SUCCESS) {
1547			msg << "Successfully subscribed to service " <<
1548				service.GetServiceType() << ", absEventSubURL: " <<
1549				service.GetAbsEventSubURL() << ".";
1550			AddLogLineU(true, logUPnP, msg);
1551		} else {
1552			msg << "Error subscribing to service " <<
1553				service.GetServiceType() << ", absEventSubURL: " <<
1554				service.GetAbsEventSubURL() << ", error: " <<
1555				m_upnpLib.GetUPnPErrorMessage(errcode) << ".";
1556			goto error;
1557		}
1558	} else {
1559		msg << "Error getting SCPD Document from " <<
1560			service.GetAbsSCPDURL() << ".";
1561		AddLogLineU(true, logUPnP, msg);
1562	}
1563
1564	return;
1565
1566	// Error processing
1567error:
1568	AddLogLineU(true, logUPnP, msg);
1569}
1570
1571
1572void CUPnPControlPoint::Unsubscribe(CUPnPService &service)
1573{
1574	ServiceMap::iterator it = m_ServiceMap.find(service.GetAbsEventSubURL());
1575	if (it != m_ServiceMap.end()) {
1576		m_ServiceMap.erase(it);
1577		UpnpUnSubscribe(m_UPnPClientHandle, service.GetSID());
1578	}
1579}
1580
1581#endif /* ENABLE_UPNP */
1582