1 /*****************************************************************
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
7 | http://www.plutinosoft.com
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
33 ****************************************************************/
35 /*----------------------------------------------------------------------
37 +---------------------------------------------------------------------*/
38 #include "PltService.h"
41 #include "PltDeviceData.h"
42 #include "PltUtilities.h"
44 NPT_SET_LOCAL_LOGGER("platinum.core.service")
46 /*----------------------------------------------------------------------
47 | PLT_Service::PLT_Service
48 +---------------------------------------------------------------------*/
49 PLT_Service::PLT_Service(PLT_DeviceData
* device
,
53 const char* last_change_namespace
/* = NULL */) :
59 m_EventingPaused(false),
60 m_LastChangeNamespace(last_change_namespace
)
62 if (name
) InitURLs(name
);
65 /*----------------------------------------------------------------------
66 | PLT_Service::~PLT_Service
67 +---------------------------------------------------------------------*/
68 PLT_Service::~PLT_Service()
73 /*----------------------------------------------------------------------
74 | PLT_Service::Cleanup
75 +---------------------------------------------------------------------*/
77 PLT_Service::Cleanup()
79 m_ActionDescs
.Apply(NPT_ObjectDeleter
<PLT_ActionDesc
>());
80 m_StateVars
.Apply(NPT_ObjectDeleter
<PLT_StateVariable
>());
82 m_ActionDescs
.Clear();
84 m_Subscribers
.Clear();
87 /*----------------------------------------------------------------------
88 | PLT_Service::GetSCPDXML
89 +---------------------------------------------------------------------*/
91 PLT_Service::GetSCPDXML(NPT_String
& scpd
)
95 // it is required to have at least 1 state variable
96 if (m_StateVars
.GetItemCount() == 0) return NPT_FAILURE
;
98 NPT_XmlElementNode
* spec
= NULL
;
99 NPT_XmlElementNode
* actionList
= NULL
;
100 NPT_XmlElementNode
* top
= new NPT_XmlElementNode("scpd");
101 NPT_XmlElementNode
* serviceStateTable
= NULL
;
102 NPT_CHECK_LABEL_SEVERE(res
= top
->SetNamespaceUri("", "urn:schemas-upnp-org:service-1-0"), cleanup
);
105 spec
= new NPT_XmlElementNode("specVersion");
106 NPT_CHECK_LABEL_SEVERE(res
= top
->AddChild(spec
), cleanup
);
107 NPT_CHECK_LABEL_SEVERE(res
= PLT_XmlHelper::AddChildText(spec
, "major", "1"), cleanup
);
108 NPT_CHECK_LABEL_SEVERE(res
= PLT_XmlHelper::AddChildText(spec
, "minor", "0"), cleanup
);
111 actionList
= new NPT_XmlElementNode("actionList");
112 NPT_CHECK_LABEL_SEVERE(res
= top
->AddChild(actionList
), cleanup
);
113 NPT_CHECK_LABEL_SEVERE(res
= m_ActionDescs
.ApplyUntil(
114 PLT_GetSCPDXMLIterator
<PLT_ActionDesc
>(actionList
),
115 NPT_UntilResultNotEquals(NPT_SUCCESS
)), cleanup
);
117 // add service state table
118 serviceStateTable
= new NPT_XmlElementNode("serviceStateTable");
119 NPT_CHECK_LABEL_SEVERE(res
= top
->AddChild(serviceStateTable
), cleanup
);
120 NPT_CHECK_LABEL_SEVERE(res
= m_StateVars
.ApplyUntil(
121 PLT_GetSCPDXMLIterator
<PLT_StateVariable
>(serviceStateTable
),
122 NPT_UntilResultNotEquals(NPT_SUCCESS
)), cleanup
);
125 NPT_CHECK_LABEL_SEVERE(res
= PLT_XmlHelper::Serialize(*top
, scpd
, true, 2), cleanup
);
132 /*----------------------------------------------------------------------
134 +---------------------------------------------------------------------*/
136 PLT_Service::GetDescription(NPT_XmlElementNode
* parent
, NPT_XmlElementNode
** service_out
/* = NULL */)
138 NPT_XmlElementNode
* service
= new NPT_XmlElementNode("service");
140 *service_out
= service
;
142 NPT_CHECK_SEVERE(parent
->AddChild(service
));
143 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service
, "serviceType", m_ServiceType
));
144 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service
, "serviceId", m_ServiceID
));
145 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service
, "SCPDURL", GetSCPDURL()));
146 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service
, "controlURL", GetControlURL()));
147 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service
, "eventSubURL", GetEventSubURL()));
152 /*----------------------------------------------------------------------
153 | PLT_Service::InitURLs
154 +---------------------------------------------------------------------*/
156 PLT_Service::InitURLs(const char* service_name
)
158 m_SCPDURL
= service_name
;
159 m_SCPDURL
+= "/" + m_Device
->GetUUID() + NPT_String("/scpd.xml");
160 m_ControlURL
= service_name
;
161 m_ControlURL
+= "/" + m_Device
->GetUUID() + NPT_String("/control.xml");
162 m_EventSubURL
= service_name
;
163 m_EventSubURL
+= "/" + m_Device
->GetUUID() + NPT_String("/event.xml");
168 /*----------------------------------------------------------------------
169 | PLT_Service::SetSCPDXML
170 +---------------------------------------------------------------------*/
172 PLT_Service::SetSCPDXML(const char* scpd
)
174 if (scpd
== NULL
) return NPT_ERROR_INVALID_PARAMETERS
;
178 NPT_XmlParser parser
;
179 NPT_XmlNode
* tree
= NULL
;
181 NPT_Array
<NPT_XmlElementNode
*> stateVariables
;
182 NPT_Array
<NPT_XmlElementNode
*> actions
;
183 NPT_XmlElementNode
* root
;
184 NPT_XmlElementNode
* actionList
;
185 NPT_XmlElementNode
* stateTable
;
187 res
= parser
.Parse(scpd
, tree
);
188 NPT_CHECK_LABEL_FATAL(res
, failure
);
190 // make sure root tag is right
191 root
= tree
->AsElementNode();
192 if (!root
|| NPT_String::Compare(root
->GetTag(), "scpd") != 0) {
193 NPT_LOG_SEVERE("Invalid scpd root tag name");
194 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
197 // make sure we have required children presents
198 stateTable
= PLT_XmlHelper::GetChild(root
, "serviceStateTable");
200 stateTable
->GetChildren().GetItemCount() == 0 ||
201 NPT_FAILED(PLT_XmlHelper::GetChildren(stateTable
,
204 NPT_LOG_SEVERE("No state variables found");
205 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
208 for (int k
= 0 ; k
< (int)stateVariables
.GetItemCount(); k
++) {
209 NPT_String name
, type
, send
;
210 PLT_XmlHelper::GetChildText(stateVariables
[k
], "name", name
);
211 PLT_XmlHelper::GetChildText(stateVariables
[k
], "dataType", type
);
212 PLT_XmlHelper::GetAttribute(stateVariables
[k
], "sendEvents", send
);
214 if (name
.GetLength() == 0 || type
.GetLength() == 0) {
215 NPT_LOG_SEVERE_1("Invalid state variable name or type at position %d", k
+1);
216 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
219 PLT_StateVariable
* variable
= new PLT_StateVariable(this);
220 m_StateVars
.Add(variable
);
222 variable
->m_Name
= name
;
223 variable
->m_DataType
= type
;
224 variable
->m_IsSendingEvents
= IsTrue(send
);
225 PLT_XmlHelper::GetChildText(stateVariables
[k
], "defaultValue", variable
->m_DefaultValue
);
227 NPT_XmlElementNode
* allowedValueList
= PLT_XmlHelper::GetChild(stateVariables
[k
], "allowedValueList");
228 if (allowedValueList
) {
229 NPT_Array
<NPT_XmlElementNode
*> allowedValues
;
230 PLT_XmlHelper::GetChildren(allowedValueList
, allowedValues
, "allowedValue");
231 for (int l
= 0 ; l
< (int)allowedValues
.GetItemCount(); l
++) {
232 const NPT_String
* text
= allowedValues
[l
]->GetText();
234 variable
->m_AllowedValues
.Add(new NPT_String(*text
));
238 NPT_XmlElementNode
* allowedValueRange
= PLT_XmlHelper::GetChild(stateVariables
[k
], "allowedValueRange");
239 if (allowedValueRange
) {
240 NPT_String min
, max
, step
;
241 PLT_XmlHelper::GetChildText(allowedValueRange
, "minimum", min
);
242 PLT_XmlHelper::GetChildText(allowedValueRange
, "maximum", max
);
243 PLT_XmlHelper::GetChildText(allowedValueRange
, "step", step
);
245 // these are required but try to be nice
246 // in case bad devices provide nothing
247 if (min
.GetLength() == 0) {
248 if (variable
->m_DataType
== "ui1" ||
249 variable
->m_DataType
== "ui2" ||
250 variable
->m_DataType
== "ui4") {
251 min
= NPT_String::FromInteger(0);
252 } else if (variable
->m_DataType
== "i1") {
253 min
= NPT_String::FromInteger(-128);
254 } else if (variable
->m_DataType
== "i2") {
255 min
= NPT_String::FromInteger(-32768);
256 } else if (variable
->m_DataType
== "i4" ||
257 variable
->m_DataType
== "int") {
258 min
= NPT_String::FromInteger(-2147483647 - 1);
260 NPT_LOG_SEVERE_1("Invalid variable data type %s", variable
->m_DataType
.GetChars());
261 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
264 if (max
.GetLength() == 0) {
265 if (variable
->m_DataType
== "ui1") {
266 max
= NPT_String::FromInteger(0xff);
267 } else if (variable
->m_DataType
== "ui2") {
268 max
= NPT_String::FromInteger(0xffff);
269 } else if (variable
->m_DataType
== "ui4") {
270 max
= NPT_String::FromInteger(0xffffffff);
271 } else if (variable
->m_DataType
== "i1") {
272 max
= NPT_String::FromInteger(0x7f);
273 } else if (variable
->m_DataType
== "i2") {
274 max
= NPT_String::FromInteger(0x7fff);
275 } else if (variable
->m_DataType
== "i4" ||
276 variable
->m_DataType
== "int") {
277 max
= NPT_String::FromInteger(0x7fffffff);
279 NPT_LOG_SEVERE_1("Invalid variable data type %s", variable
->m_DataType
.GetChars());
280 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
284 variable
->m_AllowedValueRange
= new NPT_AllowedValueRange
;
285 NPT_ParseInteger32(min
, variable
->m_AllowedValueRange
->min_value
);
286 NPT_ParseInteger32(max
, variable
->m_AllowedValueRange
->max_value
);
287 variable
->m_AllowedValueRange
->step
= -1;
289 if (step
.GetLength() != 0) {
290 NPT_ParseInteger(step
, variable
->m_AllowedValueRange
->step
);
297 actionList
= PLT_XmlHelper::GetChild(root
, "actionList");
299 res
= PLT_XmlHelper::GetChildren(actionList
, actions
, "action");
300 NPT_CHECK_LABEL_SEVERE(res
, failure
);
302 for (int i
= 0 ; i
< (int)actions
.GetItemCount(); i
++) {
303 NPT_String action_name
;
304 PLT_XmlHelper::GetChildText(actions
[i
], "name", action_name
);
305 if (action_name
.GetLength() == 0) {
306 NPT_LOG_SEVERE_1("Invalid action name for action number %d", i
+1);
307 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
310 PLT_ActionDesc
* action_desc
= new PLT_ActionDesc(action_name
, this);
311 m_ActionDescs
.Add(action_desc
);
314 NPT_XmlElementNode
* argumentList
= PLT_XmlHelper::GetChild(actions
[i
], "argumentList");
315 if (argumentList
== NULL
|| !argumentList
->GetChildren().GetItemCount())
316 continue; // no arguments is ok I guess
318 NPT_Array
<NPT_XmlElementNode
*> arguments
;
319 NPT_CHECK_LABEL_SEVERE(PLT_XmlHelper::GetChildren(argumentList
, arguments
, "argument"), failure
);
321 bool ret_value_found
= false;
322 for (int j
= 0 ; j
< (int)arguments
.GetItemCount(); j
++) {
323 NPT_String name
, direction
, relatedStateVar
;
324 PLT_XmlHelper::GetChildText(arguments
[j
], "name", name
);
325 PLT_XmlHelper::GetChildText(arguments
[j
], "direction", direction
);
326 PLT_XmlHelper::GetChildText(arguments
[j
], "relatedStateVariable", relatedStateVar
);
328 if (name
.GetLength() == 0 || direction
.GetLength() == 0 || relatedStateVar
.GetLength() == 0) {
329 NPT_LOG_SEVERE_1("Invalid argument for action %s", (const char*)action_name
);
330 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
333 // make sure the related state variable exists
334 PLT_StateVariable
* variable
= FindStateVariable(relatedStateVar
);
335 if (variable
== NULL
) {
336 NPT_LOG_SEVERE_1("State variable not found %s", (const char*)relatedStateVar
);
337 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
340 bool ret_value
= false;
341 NPT_XmlElementNode
* ret_val_node
= PLT_XmlHelper::GetChild(arguments
[j
], "retVal");
343 // verify this is the only retVal we've had
344 if (ret_value_found
) {
345 NPT_LOG_SEVERE("Return argument already found");
346 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX
, failure
);
349 ret_value_found
= true;
352 action_desc
->GetArgumentDescs().Add(new PLT_ArgumentDesc(name
, j
, direction
, variable
, ret_value
));
362 NPT_LOG_FATAL_1("Failed to parse scpd: %s", scpd
);
363 if (tree
) delete tree
;
367 /*----------------------------------------------------------------------
368 | PLT_Service::GetSCPDURL
369 +---------------------------------------------------------------------*/
371 PLT_Service::GetSCPDURL(bool absolute
/* = false */)
373 NPT_HttpUrl url
= GetDevice()->NormalizeURL(m_SCPDURL
);
374 return absolute
?url
.ToString():url
.ToRequestString();
377 /*----------------------------------------------------------------------
378 | PLT_Service::GetControlURL
379 +---------------------------------------------------------------------*/
381 PLT_Service::GetControlURL(bool absolute
/* = false */)
383 NPT_HttpUrl url
= GetDevice()->NormalizeURL(m_ControlURL
);
384 return absolute
?url
.ToString():url
.ToRequestString();
387 /*----------------------------------------------------------------------
388 | PLT_Service::GetEventSubURL
389 +---------------------------------------------------------------------*/
391 PLT_Service::GetEventSubURL(bool absolute
/* = false */)
393 NPT_HttpUrl url
= GetDevice()->NormalizeURL(m_EventSubURL
);
394 return absolute
?url
.ToString():url
.ToRequestString();
397 /*----------------------------------------------------------------------
398 | PLT_Service::ForceVersion
399 +---------------------------------------------------------------------*/
401 PLT_Service::ForceVersion(NPT_Cardinal version
)
403 if (version
< 1) return NPT_FAILURE
;
405 m_ServiceType
= m_ServiceType
.SubString(0, m_ServiceType
.GetLength()-1);
406 m_ServiceType
+= NPT_String::FromIntegerU(version
);
410 /*----------------------------------------------------------------------
411 | PLT_Service::FindActionDesc
412 +---------------------------------------------------------------------*/
414 PLT_Service::FindActionDesc(const char* name
)
416 PLT_ActionDesc
* action
= NULL
;
417 NPT_ContainerFind(m_ActionDescs
, PLT_ActionDescNameFinder(name
), action
);
421 /*----------------------------------------------------------------------
422 | PLT_Service::FindStateVariable
423 +---------------------------------------------------------------------*/
425 PLT_Service::FindStateVariable(const char* name
)
427 PLT_StateVariable
* stateVariable
= NULL
;
428 NPT_ContainerFind(m_StateVars
, PLT_StateVariableNameFinder(name
), stateVariable
);
429 return stateVariable
;
432 /*----------------------------------------------------------------------
433 | PLT_Service::GetStateVariableValue
434 +---------------------------------------------------------------------*/
436 PLT_Service::GetStateVariableValue(const char* name
, NPT_String
& value
)
438 PLT_StateVariable
* stateVariable
= FindStateVariable(name
);
439 NPT_CHECK_POINTER_FATAL(stateVariable
);
440 value
= stateVariable
->GetValue();
444 /*----------------------------------------------------------------------
445 | PLT_Service::IsSubscribable
446 +---------------------------------------------------------------------*/
448 PLT_Service::IsSubscribable()
450 NPT_List
<PLT_StateVariable
*>::Iterator var
= m_StateVars
.GetFirstItem();
452 if ((*var
)->IsSendingEvents()) return true;
458 /*----------------------------------------------------------------------
459 | PLT_Service::SetStateVariable
460 +---------------------------------------------------------------------*/
462 PLT_Service::SetStateVariable(const char* name
, const char* value
, const bool clearonsend
/*=false*/)
464 PLT_StateVariable
* stateVariable
= NULL
;
465 NPT_ContainerFind(m_StateVars
, PLT_StateVariableNameFinder(name
), stateVariable
);
466 if (stateVariable
== NULL
)
469 return stateVariable
->SetValue(value
, clearonsend
);
472 /*----------------------------------------------------------------------
473 | PLT_Service::SetStateVariableRate
474 +---------------------------------------------------------------------*/
476 PLT_Service::SetStateVariableRate(const char* name
, NPT_TimeInterval rate
)
478 PLT_StateVariable
* stateVariable
= NULL
;
479 NPT_ContainerFind(m_StateVars
, PLT_StateVariableNameFinder(name
), stateVariable
);
480 if (stateVariable
== NULL
)
483 return stateVariable
->SetRate(rate
);
486 /*----------------------------------------------------------------------
487 | PLT_Service::SetStateVariableExtraAttribute
488 +---------------------------------------------------------------------*/
490 PLT_Service::SetStateVariableExtraAttribute(const char* name
,
494 PLT_StateVariable
* stateVariable
= NULL
;
495 NPT_ContainerFind(m_StateVars
, PLT_StateVariableNameFinder(name
), stateVariable
);
496 if (stateVariable
== NULL
)
499 return stateVariable
->SetExtraAttribute(key
, value
);
502 /*----------------------------------------------------------------------
503 | PLT_Service::IncStateVariable
504 +---------------------------------------------------------------------*/
506 PLT_Service::IncStateVariable(const char* name
)
508 PLT_StateVariable
* stateVariable
= NULL
;
509 NPT_ContainerFind(m_StateVars
, PLT_StateVariableNameFinder(name
), stateVariable
);
510 if (stateVariable
== NULL
)
513 NPT_String value
= stateVariable
->GetValue();
515 if (value
.GetLength() == 0 || NPT_FAILED(value
.ToInteger(num
))) {
519 // convert value to int
520 return stateVariable
->SetValue(NPT_String::FromInteger(num
+1));
523 /*----------------------------------------------------------------------
524 | PLT_Service::ProcessNewSubscription
525 +---------------------------------------------------------------------*/
527 PLT_Service::ProcessNewSubscription(PLT_TaskManagerReference task_manager
,
528 const NPT_SocketAddress
& addr
,
529 const NPT_String
& callback_urls
,
531 NPT_HttpResponse
& response
)
533 NPT_LOG_FINE_2("New subscription for %s (timeout = %d)", m_EventSubURL
.GetChars(), timeout
);
535 // // first look if we don't have a subscriber with same callbackURL
536 // PLT_EventSubscriber* subscriber = NULL;
537 // if (NPT_SUCCEEDED(NPT_ContainerFind(m_Subscribers, PLT_EventSubscriberFinderByCallbackURL(strCallbackURL),
539 // // update local interface and timeout
540 // subscriber->m_local_if.SetIpAddress((unsigned long) addr.GetIpAddress());
541 // subscriber->m_ExpirationTime = NPT_Time(NULL) + timeout;
543 // PLT_UPnPMessageHelper::SetSID("uuid:" + subscriber->m_sid);
544 // PLT_UPnPMessageHelper::SetTimeOut(timeout);
545 // return NPT_SUCCESS;
548 // reject if we have too many subscribers already
549 if (m_Subscribers
.GetItemCount() > 30) {
550 response
.SetStatus(500, "Internal Server Error");
554 //TODO: prevent hacking by making sure callbackurl is not ourselves?
556 // generate a unique subscriber ID
558 PLT_UPnPMessageHelper::GenerateGUID(sid
);
561 PLT_EventSubscriberReference
subscriber(new PLT_EventSubscriber(task_manager
, this, sid
, timeout
));
562 // parse the callback URLs
563 bool reachable
= false;
564 if (callback_urls
[0] == '<') {
565 char* szURLs
= (char*)(const char*)callback_urls
;
566 char* brackL
= szURLs
;
567 char* brackR
= szURLs
;
568 while (++brackR
< szURLs
+ callback_urls
.GetLength()) {
569 if (*brackR
== '>') {
570 NPT_String strCallbackURL
= NPT_String(brackL
+1, (NPT_Size
)(brackR
-brackL
-1));
571 NPT_HttpUrl
url(strCallbackURL
);
574 subscriber
->AddCallbackURL(strCallbackURL
);
582 if (reachable
== false) {
583 NPT_CHECK_LABEL_FATAL(NPT_FAILURE
, cleanup
);
586 // keep track of which interface we receive the request, we will use this one
588 subscriber
->SetLocalIf(addr
);
590 PLT_UPnPMessageHelper::SetSID(response
, subscriber
->GetSID());
591 PLT_UPnPMessageHelper::SetTimeOut(response
, timeout
);
594 NPT_AutoLock
lock(m_Lock
);
596 // new subscriber should get all vars in the LastChange var
597 UpdateLastChange(m_StateVars
);
599 // send all state vars to sub immediately
600 NPT_Result res
= subscriber
->Notify(m_StateVars
);
602 // reset LastChange var to what was really just changed
603 UpdateLastChange(m_StateVarsChanged
);
605 // make sure the event worked before spawning our recurrent task
606 NPT_CHECK_LABEL_FATAL(res
, cleanup
);
608 // schedule a recurring event notification task if not running already
610 PLT_ServiceEventTask
*task
= new PLT_ServiceEventTask(this);
611 NPT_CHECK_SEVERE(task_manager
->StartTask(task
));
616 m_Subscribers
.Add(subscriber
);
622 response
.SetStatus(412, "Precondition Failed");
626 /*----------------------------------------------------------------------
627 | PLT_Service::ProcessRenewSubscription
628 +---------------------------------------------------------------------*/
630 PLT_Service::ProcessRenewSubscription(const NPT_SocketAddress
& addr
,
631 const NPT_String
& sid
,
633 NPT_HttpResponse
& response
)
635 NPT_AutoLock
lock(m_Lock
);
637 NPT_LOG_FINE_2("Renewing subscription for %s (sub=%s)",
638 m_EventSubURL
.GetChars(),
641 // first look if we don't have a subscriber with same callbackURL
642 PLT_EventSubscriberReference subscriber
;
643 if (NPT_SUCCEEDED(NPT_ContainerFind(m_Subscribers
,
644 PLT_EventSubscriberFinderBySID(sid
),
647 NPT_TimeStamp now
, expiration
;
648 NPT_System::GetCurrentTimeStamp(now
);
649 expiration
= subscriber
->GetExpirationTime();
651 // renew subscriber if it has not expired
652 if (expiration
> now
) {
653 // update local interface and timeout
654 subscriber
->SetLocalIf(addr
);
655 subscriber
->SetTimeout(timeout_secs
);
657 PLT_UPnPMessageHelper::SetSID(response
, subscriber
->GetSID());
658 PLT_UPnPMessageHelper::SetTimeOut(response
, timeout_secs
);
661 NPT_LOG_FINE_1("Subscriber \"%s\" didn't renew in time", (const char*)subscriber
->GetSID());
662 m_Subscribers
.Remove(subscriber
);
666 NPT_LOG_WARNING_1("Failed to renew subscription for %s!", sid
.GetChars());
668 // didn't find a valid Subscriber in our list
669 response
.SetStatus(412, "Precondition Failed");
673 /*----------------------------------------------------------------------
674 | PLT_Service::ProcessCancelSubscription
675 +---------------------------------------------------------------------*/
677 PLT_Service::ProcessCancelSubscription(const NPT_SocketAddress
& /* addr */,
678 const NPT_String
& sid
,
679 NPT_HttpResponse
& response
)
681 NPT_AutoLock
lock(m_Lock
);
683 // first look if we don't have a subscriber with same callbackURL
684 PLT_EventSubscriberReference sub
;
685 if (NPT_SUCCEEDED(NPT_ContainerFind(m_Subscribers
,
686 PLT_EventSubscriberFinderBySID(sid
),
688 NPT_LOG_FINE_2("Cancelling subscription for %s (sub=%s)",
689 m_EventSubURL
.GetChars(),
693 m_Subscribers
.Remove(sub
);
697 NPT_LOG_WARNING_1("Cancelling subscription for unknown subscriber %s!", sid
.GetChars());
699 // didn't find a valid Subscriber in our list
700 response
.SetStatus(412, "Precondition Failed");
704 /*----------------------------------------------------------------------
705 | PLT_Service::AddChanged
706 +---------------------------------------------------------------------*/
708 PLT_Service::AddChanged(PLT_StateVariable
* var
)
710 NPT_AutoLock
lock(m_Lock
);
712 // no event task means no subscribers yet, so don't bother
713 // Note: this will take care also when setting default state
714 // variables values during init and avoid being published
715 if (!m_EventTask
) return NPT_SUCCESS
;
717 if (var
->IsSendingEvents()) {
718 if (!m_StateVarsToPublish
.Contains(var
)) m_StateVarsToPublish
.Add(var
);
719 } else if (var
->IsSendingEvents(true)) {
720 if (!m_StateVarsChanged
.Contains(var
)) m_StateVarsChanged
.Add(var
);
722 UpdateLastChange(m_StateVarsChanged
);
728 /*----------------------------------------------------------------------
729 | PLT_Service::UpdateLastChange
730 +---------------------------------------------------------------------*/
732 PLT_Service::UpdateLastChange(NPT_List
<PLT_StateVariable
*>& vars
)
734 PLT_StateVariable
* var
= FindStateVariable("LastChange");
735 if (var
== NULL
) return NPT_FAILURE
;
737 NPT_ASSERT(m_LastChangeNamespace
.GetLength() > 0);
739 if (vars
.GetItemCount() == 0) {
740 // no vars to update, remove LastChange from vars to publish
741 m_StateVarsToPublish
.Remove(var
);
745 NPT_Reference
<NPT_XmlElementNode
> top(new NPT_XmlElementNode("Event"));
746 NPT_CHECK_SEVERE(top
->SetNamespaceUri("", m_LastChangeNamespace
));
748 NPT_XmlElementNode
* instance
= new NPT_XmlElementNode("InstanceID");
749 NPT_CHECK_SEVERE(top
->AddChild(instance
));
750 NPT_CHECK_SEVERE(instance
->SetAttribute("val", "0"));
752 // build list of changes
753 NPT_CHECK_SEVERE(vars
.ApplyUntil(
754 PLT_LastChangeXMLIterator(instance
),
755 NPT_UntilResultNotEquals(NPT_SUCCESS
)));
759 NPT_CHECK_SEVERE(PLT_XmlHelper::Serialize(*top
, value
, false));
761 // set the state change directly instead of calling SetValue
762 // to avoid recursive lock, instead add LastChange var directly
763 var
->m_Value
= value
;
765 // add to list of vars scheduled to be published next time if not already there
766 if (!m_StateVarsToPublish
.Contains(var
)) m_StateVarsToPublish
.Add(var
);
770 /*----------------------------------------------------------------------
771 | PLT_Service::PauseEventing
772 +---------------------------------------------------------------------*/
774 PLT_Service::PauseEventing(bool pause
/* = TRUE */)
776 NPT_AutoLock
lock(m_Lock
);
777 m_EventingPaused
= pause
;
781 /*----------------------------------------------------------------------
782 | PLT_Service::NotifyChanged
783 +---------------------------------------------------------------------*/
785 PLT_Service::NotifyChanged()
787 NPT_AutoLock
lock(m_Lock
);
789 // no eventing for now
790 if (m_EventingPaused
) return NPT_SUCCESS
;
792 // pick the vars that are ready to be published
793 // based on their moderation rate and last publication
794 NPT_List
<PLT_StateVariable
*> vars_ready
;
795 NPT_List
<PLT_StateVariable
*>::Iterator iter
= m_StateVarsToPublish
.GetFirstItem();
797 PLT_StateVariable
* var
= *iter
;
798 if (var
->IsReadyToPublish()) {
800 m_StateVarsToPublish
.Erase(iter
++);
802 // clear last changed list if we're about to send LastChange var
803 if (!var
->GetName().Compare("LastChange")) m_StateVarsChanged
.Clear();
811 // if nothing to publish then bail out now
812 // we'll clean up expired subscribers when we have something to publish
813 if (vars_ready
.GetItemCount() == 0) return NPT_SUCCESS
;
815 // send vars that are ready to go and remove old subscribers
816 NPT_List
<PLT_EventSubscriberReference
>::Iterator sub_iter
= m_Subscribers
.GetFirstItem();
818 PLT_EventSubscriberReference sub
= *sub_iter
;
820 NPT_TimeStamp now
, expiration
;
821 NPT_System::GetCurrentTimeStamp(now
);
822 expiration
= sub
->GetExpirationTime();
824 // forget sub if it didn't renew subscription in time or if notification failed
825 if (expiration
== NPT_TimeStamp() || now
< expiration
+ NPT_TimeStamp(30.f
)) {
826 // TODO: Notification is asynchronous, so we won't know if it failed until
827 // the subscriber m_SubscriberTask is done
828 NPT_Result res
= vars_ready
.GetItemCount()?sub
->Notify(vars_ready
):NPT_SUCCESS
;
829 if (NPT_SUCCEEDED(res
)) {
835 m_Subscribers
.Erase(sub_iter
++);
838 // some state variables must be cleared immediatly after sending
839 iter
= vars_ready
.GetFirstItem();
841 PLT_StateVariable
* var
= *iter
;
842 var
->OnSendCompleted();
848 /*----------------------------------------------------------------------
849 | PLT_ServiceSCPDURLFinder::operator()
850 +---------------------------------------------------------------------*/
852 PLT_ServiceSCPDURLFinder::operator()(PLT_Service
* const & service
) const
854 return m_URL
.Compare(service
->GetSCPDURL(m_URL
.StartsWith("http://")?true:false), true)?false:true;
857 /*----------------------------------------------------------------------
858 | PLT_ServiceControlURLFinder::operator()
859 +---------------------------------------------------------------------*/
861 PLT_ServiceControlURLFinder::operator()(PLT_Service
* const & service
) const
863 return m_URL
.Compare(service
->GetControlURL(m_URL
.StartsWith("http://")?true:false), true)?false:true;
866 /*----------------------------------------------------------------------
867 | PLT_ServiceEventSubURLFinder::operator()
868 +---------------------------------------------------------------------*/
870 PLT_ServiceEventSubURLFinder::operator()(PLT_Service
* const & service
) const
872 return m_URL
.Compare(service
->GetEventSubURL(m_URL
.StartsWith("http://")?true:false), true)?false:true;
875 /*----------------------------------------------------------------------
876 | PLT_ServiceIDFinder::operator()
877 +---------------------------------------------------------------------*/
879 PLT_ServiceIDFinder::operator()(PLT_Service
* const & service
) const
881 return m_Id
.Compare(service
->GetServiceID(), true) ? false : true;
884 /*----------------------------------------------------------------------
885 | PLT_ServiceTypeFinder::operator()
886 +---------------------------------------------------------------------*/
888 PLT_ServiceTypeFinder::operator()(PLT_Service
* const & service
) const
890 // DLNA: match any version if last char is '*'
891 if (m_Type
.EndsWith("*")) {
892 return m_Type
.CompareN(service
->GetServiceType(), m_Type
.GetLength()-1, true) ? false : true;
895 return m_Type
.Compare(service
->GetServiceType(), true) ? false : true;
898 /*----------------------------------------------------------------------
899 | PLT_ServiceNameFinder::operator()
900 +---------------------------------------------------------------------*/
902 PLT_ServiceNameFinder::operator()(PLT_Service
* const & service
) const
904 return m_Name
.Compare(service
->GetServiceName(), true) ? false : true;
907 /*----------------------------------------------------------------------
908 | PLT_GetLastChangeXMLIterator::operator()
909 +---------------------------------------------------------------------*/
911 PLT_LastChangeXMLIterator::operator()(PLT_StateVariable
* const &var
) const
913 // only add vars that are indirectly evented
914 if (!var
->IsSendingEvents(true)) return NPT_SUCCESS
;
916 NPT_XmlElementNode
* variable
= new NPT_XmlElementNode((const char*)var
->GetName());
917 NPT_CHECK_SEVERE(m_Node
->AddChild(variable
));
918 NPT_CHECK_SEVERE(var
->Serialize(*variable
));