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