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