Merge pull request #25959 from neo1973/TagLib_deprecation_warnings
[xbmc.git] / lib / libUPnP / Platinum / Source / Core / PltService.cpp
blobb86fb2379f38ca639ac52082e523d2542d3e6d0d
1 /*****************************************************************
3 | Platinum - Service
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
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 /*----------------------------------------------------------------------
36 | includes
37 +---------------------------------------------------------------------*/
38 #include "PltService.h"
39 #include "PltSsdp.h"
40 #include "PltUPnP.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,
50 const char* type,
51 const char* id,
52 const char* name,
53 const char* last_change_namespace /* = NULL */) :
54 m_Device(device),
55 m_ServiceType(type),
56 m_ServiceID(id),
57 m_ServiceName(name),
58 m_EventTask(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()
70 Cleanup();
73 /*----------------------------------------------------------------------
74 | PLT_Service::Cleanup
75 +---------------------------------------------------------------------*/
76 void
77 PLT_Service::Cleanup()
79 m_ActionDescs.Apply(NPT_ObjectDeleter<PLT_ActionDesc>());
80 m_StateVars.Apply(NPT_ObjectDeleter<PLT_StateVariable>());
82 m_ActionDescs.Clear();
83 m_StateVars.Clear();
84 m_Subscribers.Clear();
87 /*----------------------------------------------------------------------
88 | PLT_Service::GetSCPDXML
89 +---------------------------------------------------------------------*/
90 NPT_Result
91 PLT_Service::GetSCPDXML(NPT_String& scpd)
93 NPT_Result res;
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);
104 // add spec version
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);
110 // add actions
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);
124 // serialize node
125 NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::Serialize(*top, scpd, true, 2), cleanup);
127 cleanup:
128 delete top;
129 return res;
132 /*----------------------------------------------------------------------
133 | PLT_Service::ToXML
134 +---------------------------------------------------------------------*/
135 NPT_Result
136 PLT_Service::GetDescription(NPT_XmlElementNode* parent, NPT_XmlElementNode** service_out /* = NULL */)
138 NPT_XmlElementNode* service = new NPT_XmlElementNode("service");
139 if (service_out) {
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()));
149 return NPT_SUCCESS;
152 /*----------------------------------------------------------------------
153 | PLT_Service::InitURLs
154 +---------------------------------------------------------------------*/
155 NPT_Result
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");
165 return NPT_SUCCESS;
168 /*----------------------------------------------------------------------
169 | PLT_Service::SetSCPDXML
170 +---------------------------------------------------------------------*/
171 NPT_Result
172 PLT_Service::SetSCPDXML(const char* scpd)
174 if (scpd == NULL) return NPT_ERROR_INVALID_PARAMETERS;
176 Cleanup();
178 NPT_XmlParser parser;
179 NPT_XmlNode* tree = NULL;
180 NPT_Result res;
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");
199 if (!stateTable ||
200 stateTable->GetChildren().GetItemCount() == 0 ||
201 NPT_FAILED(PLT_XmlHelper::GetChildren(stateTable,
202 stateVariables,
203 "stateVariable"))) {
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();
233 if (text) {
234 variable->m_AllowedValues.Add(new NPT_String(*text));
237 } else {
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);
259 } else {
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);
278 } else {
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);
296 // actions
297 actionList = PLT_XmlHelper::GetChild(root, "actionList");
298 if (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);
313 // action arguments
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");
342 if (ret_val_node) {
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);
347 } else {
348 ret_value = true;
349 ret_value_found = true;
352 action_desc->GetArgumentDescs().Add(new PLT_ArgumentDesc(name, j, direction, variable, ret_value));
357 // delete the tree
358 delete tree;
359 return NPT_SUCCESS;
361 failure:
362 NPT_LOG_FATAL_1("Failed to parse scpd: %s", scpd);
363 if (tree) delete tree;
364 return NPT_FAILURE;
367 /*----------------------------------------------------------------------
368 | PLT_Service::GetSCPDURL
369 +---------------------------------------------------------------------*/
370 NPT_String
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 +---------------------------------------------------------------------*/
380 NPT_String
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 +---------------------------------------------------------------------*/
390 NPT_String
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 +---------------------------------------------------------------------*/
400 NPT_Result
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);
407 return NPT_SUCCESS;
410 /*----------------------------------------------------------------------
411 | PLT_Service::FindActionDesc
412 +---------------------------------------------------------------------*/
413 PLT_ActionDesc*
414 PLT_Service::FindActionDesc(const char* name)
416 PLT_ActionDesc* action = NULL;
417 NPT_ContainerFind(m_ActionDescs, PLT_ActionDescNameFinder(name), action);
418 return action;
421 /*----------------------------------------------------------------------
422 | PLT_Service::FindStateVariable
423 +---------------------------------------------------------------------*/
424 PLT_StateVariable*
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 +---------------------------------------------------------------------*/
435 NPT_Result
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();
441 return NPT_SUCCESS;
444 /*----------------------------------------------------------------------
445 | PLT_Service::IsSubscribable
446 +---------------------------------------------------------------------*/
447 bool
448 PLT_Service::IsSubscribable()
450 NPT_List<PLT_StateVariable*>::Iterator var = m_StateVars.GetFirstItem();
451 while (var) {
452 if ((*var)->IsSendingEvents()) return true;
453 ++var;
455 return false;
458 /*----------------------------------------------------------------------
459 | PLT_Service::SetStateVariable
460 +---------------------------------------------------------------------*/
461 NPT_Result
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)
467 return NPT_FAILURE;
469 return stateVariable->SetValue(value, clearonsend);
472 /*----------------------------------------------------------------------
473 | PLT_Service::SetStateVariableRate
474 +---------------------------------------------------------------------*/
475 NPT_Result
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)
481 return NPT_FAILURE;
483 return stateVariable->SetRate(rate);
486 /*----------------------------------------------------------------------
487 | PLT_Service::SetStateVariableExtraAttribute
488 +---------------------------------------------------------------------*/
489 NPT_Result
490 PLT_Service::SetStateVariableExtraAttribute(const char* name,
491 const char* key,
492 const char* value)
494 PLT_StateVariable* stateVariable = NULL;
495 NPT_ContainerFind(m_StateVars, PLT_StateVariableNameFinder(name), stateVariable);
496 if (stateVariable == NULL)
497 return NPT_FAILURE;
499 return stateVariable->SetExtraAttribute(key, value);
502 /*----------------------------------------------------------------------
503 | PLT_Service::IncStateVariable
504 +---------------------------------------------------------------------*/
505 NPT_Result
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)
511 return NPT_FAILURE;
513 NPT_String value = stateVariable->GetValue();
514 NPT_Int32 num;
515 if (value.GetLength() == 0 || NPT_FAILED(value.ToInteger(num))) {
516 return NPT_FAILURE;
519 // convert value to int
520 return stateVariable->SetValue(NPT_String::FromInteger(num+1));
523 /*----------------------------------------------------------------------
524 | PLT_Service::ProcessNewSubscription
525 +---------------------------------------------------------------------*/
526 NPT_Result
527 PLT_Service::ProcessNewSubscription(PLT_TaskManagerReference task_manager,
528 const NPT_SocketAddress& addr,
529 const NPT_String& callback_urls,
530 int timeout,
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),
538 // subscriber))) {
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;
546 // }
548 // reject if we have too many subscribers already
549 if (m_Subscribers.GetItemCount() > 30) {
550 response.SetStatus(500, "Internal Server Error");
551 return NPT_FAILURE;
554 //TODO: prevent hacking by making sure callbackurl is not ourselves?
556 // generate a unique subscriber ID
557 NPT_String sid;
558 PLT_UPnPMessageHelper::GenerateGUID(sid);
559 sid = "uuid:" + 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);
573 if (url.IsValid()) {
574 subscriber->AddCallbackURL(strCallbackURL);
575 reachable = true;
577 brackL = ++brackR;
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
587 // when notifying
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
609 if (!m_EventTask) {
610 PLT_ServiceEventTask *task = new PLT_ServiceEventTask(this);
611 NPT_CHECK_SEVERE(task_manager->StartTask(task));
613 m_EventTask = task;
616 m_Subscribers.Add(subscriber);
619 return NPT_SUCCESS;
621 cleanup:
622 response.SetStatus(412, "Precondition Failed");
623 return NPT_FAILURE;
626 /*----------------------------------------------------------------------
627 | PLT_Service::ProcessRenewSubscription
628 +---------------------------------------------------------------------*/
629 NPT_Result
630 PLT_Service::ProcessRenewSubscription(const NPT_SocketAddress& addr,
631 const NPT_String& sid,
632 int timeout_secs,
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(),
639 sid.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),
645 subscriber))) {
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);
659 return NPT_SUCCESS;
660 } else {
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");
670 return NPT_FAILURE;
673 /*----------------------------------------------------------------------
674 | PLT_Service::ProcessCancelSubscription
675 +---------------------------------------------------------------------*/
676 NPT_Result
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),
687 sub))) {
688 NPT_LOG_FINE_2("Cancelling subscription for %s (sub=%s)",
689 m_EventSubURL.GetChars(),
690 sid.GetChars());
692 // remove sub
693 m_Subscribers.Remove(sub);
694 return NPT_SUCCESS;
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");
701 return NPT_FAILURE;
704 /*----------------------------------------------------------------------
705 | PLT_Service::AddChanged
706 +---------------------------------------------------------------------*/
707 NPT_Result
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);
725 return NPT_SUCCESS;
728 /*----------------------------------------------------------------------
729 | PLT_Service::UpdateLastChange
730 +---------------------------------------------------------------------*/
731 NPT_Result
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);
742 return NPT_SUCCESS;
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)));
757 // serialize node
758 NPT_String value;
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);
767 return NPT_SUCCESS;
770 /*----------------------------------------------------------------------
771 | PLT_Service::PauseEventing
772 +---------------------------------------------------------------------*/
773 NPT_Result
774 PLT_Service::PauseEventing(bool pause /* = TRUE */)
776 NPT_AutoLock lock(m_Lock);
777 m_EventingPaused = pause;
778 return NPT_SUCCESS;
781 /*----------------------------------------------------------------------
782 | PLT_Service::NotifyChanged
783 +---------------------------------------------------------------------*/
784 NPT_Result
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();
796 while (iter) {
797 PLT_StateVariable* var = *iter;
798 if (var->IsReadyToPublish()) {
799 vars_ready.Add(var);
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();
805 continue;
808 ++iter;
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();
817 while (sub_iter) {
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)) {
830 ++sub_iter;
831 continue;
835 m_Subscribers.Erase(sub_iter++);
838 // some state variables must be cleared immediatly after sending
839 iter = vars_ready.GetFirstItem();
840 while (iter) {
841 PLT_StateVariable* var = *iter;
842 var->OnSendCompleted();
843 ++iter;
845 return NPT_SUCCESS;
848 /*----------------------------------------------------------------------
849 | PLT_ServiceSCPDURLFinder::operator()
850 +---------------------------------------------------------------------*/
851 bool
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 +---------------------------------------------------------------------*/
860 bool
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 +---------------------------------------------------------------------*/
869 bool
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 +---------------------------------------------------------------------*/
878 bool
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 +---------------------------------------------------------------------*/
887 bool
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 +---------------------------------------------------------------------*/
901 bool
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 +---------------------------------------------------------------------*/
910 NPT_Result
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));
919 return NPT_SUCCESS;