comparison deps/Platinum/Source/Core/PltService.cpp @ 0:3425707ddbf6

Initial import (hopefully this mercurial stuff works...)
author fraserofthenight
date Mon, 06 Jul 2009 08:06:28 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:3425707ddbf6
1 /*****************************************************************
2 |
3 | Platinum - Service
4 |
5 | Copyright (c) 2004-2008, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
8 |
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.
13 |
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 |
21 | This program is distributed in the hope that it will be useful,
22 | but WITHOUT ANY WARRANTY; without even the implied warranty of
23 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 | GNU General Public License for more details.
25 |
26 | You should have received a copy of the GNU General Public License
27 | along with this program; see the file LICENSE.txt. If not, write to
28 | the Free Software Foundation, Inc.,
29 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 | http://www.gnu.org/licenses/gpl-2.0.html
31 |
32 ****************************************************************/
33
34 /*----------------------------------------------------------------------
35 | includes
36 +---------------------------------------------------------------------*/
37 #include "PltService.h"
38 #include "PltSsdp.h"
39 #include "PltUPnP.h"
40 #include "PltDeviceData.h"
41 #include "PltXmlHelper.h"
42
43 NPT_SET_LOCAL_LOGGER("platinum.core.service")
44
45 /*----------------------------------------------------------------------
46 | PLT_Service::PLT_Service
47 +---------------------------------------------------------------------*/
48 PLT_Service::PLT_Service(PLT_DeviceData* device,
49 const char* type,
50 const char* id,
51 const char* last_change_namespace /* = NULL */) :
52 m_Device(device),
53 m_ServiceType(type),
54 m_ServiceID(id),
55 m_EventTask(NULL),
56 m_EventingPaused(false),
57 m_LastChangeNamespace(last_change_namespace)
58 {
59 }
60
61 /*----------------------------------------------------------------------
62 | PLT_Service::~PLT_Service
63 +---------------------------------------------------------------------*/
64 PLT_Service::~PLT_Service()
65 {
66 Cleanup();
67 }
68
69 /*----------------------------------------------------------------------
70 | PLT_Service::~PLT_Service
71 +---------------------------------------------------------------------*/
72 void
73 PLT_Service::Cleanup()
74 {
75 m_ActionDescs.Apply(NPT_ObjectDeleter<PLT_ActionDesc>());
76 m_StateVars.Apply(NPT_ObjectDeleter<PLT_StateVariable>());
77 m_Subscribers.Apply(NPT_ObjectDeleter<PLT_EventSubscriber>());
78
79 m_ActionDescs.Clear();
80 m_StateVars.Clear();
81 m_Subscribers.Clear();
82 }
83
84 /*----------------------------------------------------------------------
85 | PLT_Service::GetSCPDXML
86 +---------------------------------------------------------------------*/
87 NPT_Result
88 PLT_Service::GetSCPDXML(NPT_String& scpd)
89 {
90 NPT_Result res;
91
92 // it is required to have at least 1 state variable
93 if (m_StateVars.GetItemCount() == 0) return NPT_FAILURE;
94
95 NPT_XmlElementNode* spec = NULL;
96 NPT_XmlElementNode* actionList = NULL;
97 NPT_XmlElementNode* top = new NPT_XmlElementNode("scpd");
98 NPT_XmlElementNode* serviceStateTable = NULL;
99 NPT_CHECK_LABEL_SEVERE(res = top->SetNamespaceUri("", "urn:schemas-upnp-org:service-1-0"), cleanup);
100
101 // add spec version
102 spec = new NPT_XmlElementNode("specVersion");
103 NPT_CHECK_LABEL_SEVERE(res = top->AddChild(spec), cleanup);
104 NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::AddChildText(spec, "major", "1"), cleanup);
105 NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::AddChildText(spec, "minor", "0"), cleanup);
106
107 // add actions
108 actionList = new NPT_XmlElementNode("actionList");
109 NPT_CHECK_LABEL_SEVERE(res = top->AddChild(actionList), cleanup);
110 NPT_CHECK_LABEL_SEVERE(res = m_ActionDescs.ApplyUntil(PLT_GetSCPDXMLIterator<PLT_ActionDesc>(actionList),
111 NPT_UntilResultNotEquals(NPT_SUCCESS)), cleanup);
112
113 // add service state table
114 serviceStateTable = new NPT_XmlElementNode("serviceStateTable");
115 NPT_CHECK_LABEL_SEVERE(res = top->AddChild(serviceStateTable), cleanup);
116 NPT_CHECK_LABEL_SEVERE(res = m_StateVars.ApplyUntil(PLT_GetSCPDXMLIterator<PLT_StateVariable>(serviceStateTable),
117 NPT_UntilResultNotEquals(NPT_SUCCESS)), cleanup);
118
119 // serialize node
120 NPT_CHECK_LABEL_SEVERE(res = PLT_XmlHelper::Serialize(*top, scpd), cleanup);
121
122 cleanup:
123 delete top;
124 return res;
125 }
126
127 /*----------------------------------------------------------------------
128 | PLT_Service::ToXML
129 +---------------------------------------------------------------------*/
130 NPT_Result
131 PLT_Service::GetDescription(NPT_XmlElementNode* parent, NPT_XmlElementNode** service_out /* = NULL */)
132 {
133 NPT_XmlElementNode* service = new NPT_XmlElementNode("service");
134 if (service_out) {
135 *service_out = service;
136 }
137 NPT_CHECK_SEVERE(parent->AddChild(service));
138 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service, "serviceType", m_ServiceType));
139 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service, "serviceId", m_ServiceID));
140 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service, "SCPDURL", GetSCPDURL()));
141 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service, "controlURL", GetControlURL()));
142 NPT_CHECK_SEVERE(PLT_XmlHelper::AddChildText(service, "eventSubURL", GetEventSubURL()));
143
144 return NPT_SUCCESS;
145 }
146
147 /*----------------------------------------------------------------------
148 | PLT_Service::InitURLs
149 +---------------------------------------------------------------------*/
150 NPT_Result
151 PLT_Service::InitURLs(const char* service_name,
152 const char* device_uuid)
153 {
154 m_SCPDURL = service_name;
155 m_SCPDURL += "/" + NPT_String(device_uuid) + NPT_String("/scpd.xml");
156 m_ControlURL = service_name;
157 m_ControlURL += "/" + NPT_String(device_uuid) + NPT_String("/control.xml");
158 m_EventSubURL = service_name;
159 m_EventSubURL += "/" + NPT_String(device_uuid) + NPT_String("/event.xml");
160
161 return NPT_SUCCESS;
162 }
163
164 /*----------------------------------------------------------------------
165 | PLT_Service::SetSCPDXML
166 +---------------------------------------------------------------------*/
167 NPT_Result
168 PLT_Service::SetSCPDXML(const char* scpd)
169 {
170 if (scpd == NULL) return NPT_FAILURE;
171
172 Cleanup();
173
174 NPT_XmlParser parser;
175 NPT_XmlNode* tree = NULL;
176 NPT_Result res;
177 NPT_Array<NPT_XmlElementNode*> stateVariables;
178 NPT_Array<NPT_XmlElementNode*> actions;
179 NPT_XmlElementNode* root;
180 NPT_XmlElementNode* actionList;
181 NPT_XmlElementNode* stateTable;
182
183 res = parser.Parse(scpd, tree);
184 NPT_CHECK_LABEL_FATAL(res, failure);
185
186 // make sure root tag is right
187 root = tree->AsElementNode();
188 if (!root || NPT_String::Compare(root->GetTag(), "scpd")) {
189 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
190 }
191
192 // make sure we have required children presents
193 stateTable = PLT_XmlHelper::GetChild(root, "serviceStateTable");
194 if (!stateTable || stateTable->GetChildren().GetItemCount() == 0) {
195 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
196 }
197
198 // stateVariable table
199 if (NPT_FAILED(PLT_XmlHelper::GetChildren(stateTable,
200 stateVariables,
201 "stateVariable"))) {
202 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
203 }
204
205 for (int k = 0 ; k < (int)stateVariables.GetItemCount(); k++) {
206
207 NPT_String name, type, send;
208 PLT_XmlHelper::GetChildText(stateVariables[k], "name", name);
209 PLT_XmlHelper::GetChildText(stateVariables[k], "dataType", type);
210 PLT_XmlHelper::GetAttribute(stateVariables[k], "sendEvents", send);
211
212 if (name.GetLength() == 0 || type.GetLength() == 0) {
213 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
214 }
215
216 PLT_StateVariable* variable = new PLT_StateVariable(this);
217 m_StateVars.Add(variable);
218
219 variable->m_Name = name;
220 variable->m_DataType = type;
221 variable->m_IsSendingEvents = IsTrue(send); // could it be true/false ?
222 PLT_XmlHelper::GetChildText(stateVariables[k], "defaultValue", variable->m_DefaultValue);
223
224 NPT_XmlElementNode* allowedValueList = PLT_XmlHelper::GetChild(stateVariables[k], "allowedValueList");
225 if (allowedValueList) {
226 NPT_Array<NPT_XmlElementNode*> allowedValues;
227 PLT_XmlHelper::GetChildren(allowedValueList, allowedValues, "allowedValue");
228 for (int l = 0 ; l < (int)allowedValues.GetItemCount(); l++) {
229 const NPT_String* text = allowedValues[l]->GetText();
230 if (text) {
231 variable->m_AllowedValues.Add(new NPT_String(*text));
232 }
233 }
234 } else {
235 NPT_XmlElementNode* allowedValueRange = PLT_XmlHelper::GetChild(stateVariables[k], "allowedValueRange");
236 if (allowedValueRange) {
237 NPT_String min, max, step;
238 PLT_XmlHelper::GetChildText(allowedValueRange, "minimum", min);
239 PLT_XmlHelper::GetChildText(allowedValueRange, "maximum", max);
240 PLT_XmlHelper::GetChildText(allowedValueRange, "step", step);
241
242 if (min.GetLength() == 0 || max.GetLength() == 0) {
243 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
244 }
245
246 variable->m_AllowedValueRange = new NPT_AllowedValueRange;
247 NPT_ParseInteger32(min, variable->m_AllowedValueRange->min_value);
248 NPT_ParseInteger32(max, variable->m_AllowedValueRange->max_value);
249 variable->m_AllowedValueRange->step = -1;
250
251 if (step.GetLength() != 0) {
252 NPT_ParseInteger(step, variable->m_AllowedValueRange->step);
253 }
254 }
255 }
256 }
257
258 // actions
259 actionList = PLT_XmlHelper::GetChild(root, "actionList");
260 if (actionList) {
261 if (NPT_FAILED(PLT_XmlHelper::GetChildren(actionList, actions, "action"))) {
262 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
263 }
264
265 for (int i = 0 ; i < (int)actions.GetItemCount(); i++) {
266 NPT_String action_name;
267 PLT_XmlHelper::GetChildText(actions[i], "name", action_name);
268 if (action_name.GetLength() == 0) {
269 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
270 }
271
272 PLT_ActionDesc* action_desc = new PLT_ActionDesc(action_name, this);
273 m_ActionDescs.Add(action_desc);
274
275 // action arguments
276 NPT_XmlElementNode* argumentList = PLT_XmlHelper::GetChild(actions[i], "argumentList");
277 if (argumentList == NULL || !argumentList->GetChildren().GetItemCount())
278 continue; // no arguments is ok I guess
279
280 NPT_Array<NPT_XmlElementNode*> arguments;
281 NPT_CHECK_LABEL_SEVERE(PLT_XmlHelper::GetChildren(argumentList, arguments, "argument"), failure);
282
283 bool foundRetValue = false;
284 for (int j = 0 ; j < (int)arguments.GetItemCount(); j++) {
285 NPT_String name, direction, relatedStateVar;
286 PLT_XmlHelper::GetChildText(arguments[j], "name", name);
287 PLT_XmlHelper::GetChildText(arguments[j], "direction", direction);
288 PLT_XmlHelper::GetChildText(arguments[j], "relatedStateVariable", relatedStateVar);
289
290 if (name.GetLength() == 0 || direction.GetLength() == 0 || relatedStateVar.GetLength() == 0) {
291 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
292 }
293
294 // make sure the related state variable exists
295 PLT_StateVariable* variable = FindStateVariable(relatedStateVar);
296 if (variable == NULL) {
297 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
298 }
299
300 bool bReturnValue = false;
301 NPT_XmlElementNode* retval_node = PLT_XmlHelper::GetChild(arguments[j], "retVal");
302 if (retval_node) {
303 // verify this is the only retVal we've had
304 if (foundRetValue) {
305 NPT_CHECK_LABEL_SEVERE(NPT_ERROR_INVALID_SYNTAX, failure);
306 } else {
307 bReturnValue = true;
308 foundRetValue = true;
309 }
310 }
311 action_desc->GetArgumentDescs().Add(new PLT_ArgumentDesc(name, direction, variable, bReturnValue));
312 }
313 }
314 }
315
316 // delete the tree
317 delete tree;
318 return NPT_SUCCESS;
319
320 failure:
321 NPT_LOG_FATAL_1("Failed to parse scpd: %s", scpd);
322 delete tree;
323 return NPT_FAILURE;
324 }
325
326 /*----------------------------------------------------------------------
327 | PLT_Service::FindActionDesc
328 +---------------------------------------------------------------------*/
329 PLT_ActionDesc*
330 PLT_Service::FindActionDesc(const char* name)
331 {
332 PLT_ActionDesc* action = NULL;
333 NPT_ContainerFind(m_ActionDescs, PLT_ActionDescNameFinder(this, name), action);
334 return action;
335 }
336
337 /*----------------------------------------------------------------------
338 | PLT_Service::FindStateVariable
339 +---------------------------------------------------------------------*/
340 PLT_StateVariable*
341 PLT_Service::FindStateVariable(const char* name)
342 {
343 PLT_StateVariable* stateVariable = NULL;
344 NPT_ContainerFind(m_StateVars, PLT_StateVariableNameFinder(name), stateVariable);
345 return stateVariable;
346 }
347
348 /*----------------------------------------------------------------------
349 | PLT_Service::GetStateVariableValue
350 +---------------------------------------------------------------------*/
351 NPT_Result
352 PLT_Service::GetStateVariableValue(const char* name, NPT_String& value)
353 {
354 PLT_StateVariable* stateVariable = FindStateVariable(name);
355 NPT_CHECK_POINTER_FATAL(stateVariable);
356 value = stateVariable->GetValue();
357 return NPT_SUCCESS;
358 }
359
360 /*----------------------------------------------------------------------
361 | PLT_Service::IsSubscribable
362 +---------------------------------------------------------------------*/
363 bool
364 PLT_Service::IsSubscribable()
365 {
366 NPT_List<PLT_StateVariable*>::Iterator var = m_StateVars.GetFirstItem();
367 while (var) {
368 if ((*var)->IsSendingEvents()) return true;
369 ++var;
370 }
371 return false;
372 }
373
374 /*----------------------------------------------------------------------
375 | PLT_Service::SetStateVariable
376 +---------------------------------------------------------------------*/
377 NPT_Result
378 PLT_Service::SetStateVariable(const char* name, const char* value)
379 {
380 PLT_StateVariable* stateVariable = NULL;
381 NPT_ContainerFind(m_StateVars, PLT_StateVariableNameFinder(name), stateVariable);
382 if (stateVariable == NULL)
383 return NPT_FAILURE;
384
385 return stateVariable->SetValue(value);
386 }
387
388 /*----------------------------------------------------------------------
389 | PLT_Service::SetStateVariableRate
390 +---------------------------------------------------------------------*/
391 NPT_Result
392 PLT_Service::SetStateVariableRate(const char* name, NPT_TimeInterval rate)
393 {
394 PLT_StateVariable* stateVariable = NULL;
395 NPT_ContainerFind(m_StateVars, PLT_StateVariableNameFinder(name), stateVariable);
396 if (stateVariable == NULL)
397 return NPT_FAILURE;
398
399 return stateVariable->SetRate(rate);
400 }
401
402 /*----------------------------------------------------------------------
403 | PLT_Service::IncStateVariable
404 +---------------------------------------------------------------------*/
405 NPT_Result
406 PLT_Service::IncStateVariable(const char* name)
407 {
408 PLT_StateVariable* stateVariable = NULL;
409 NPT_ContainerFind(m_StateVars, PLT_StateVariableNameFinder(name), stateVariable);
410 if (stateVariable == NULL)
411 return NPT_FAILURE;
412
413 NPT_String value = stateVariable->GetValue();
414 NPT_Int32 num;
415 if (value.GetLength() == 0 || NPT_FAILED(value.ToInteger(num))) {
416 return NPT_FAILURE;
417 }
418
419 // convert value to int
420 return stateVariable->SetValue(NPT_String::FromInteger(num+1));
421 }
422
423 /*----------------------------------------------------------------------
424 | PLT_Service::ProcessNewSubscription
425 +---------------------------------------------------------------------*/
426 NPT_Result
427 PLT_Service::ProcessNewSubscription(PLT_TaskManager* task_manager,
428 const NPT_SocketAddress& addr,
429 const NPT_String& callback_urls,
430 int timeout,
431 NPT_HttpResponse& response)
432 {
433 NPT_LOG_FINE_1("New subscription for %s", m_EventSubURL.GetChars());
434
435 // // first look if we don't have a subscriber with same callbackURL
436 // PLT_EventSubscriber* subscriber = NULL;
437 // if (NPT_SUCCEEDED(NPT_ContainerFind(m_Subscribers, PLT_EventSubscriberFinderByCallbackURL(strCallbackURL),
438 // subscriber))) {
439 // // update local interface and timeout
440 // subscriber->m_local_if.SetIpAddress((unsigned long) addr.GetIpAddress());
441 // subscriber->m_ExpirationTime = NPT_Time(NULL) + timeout;
442 //
443 // PLT_UPnPMessageHelper::SetSID("uuid:" + subscriber->m_sid);
444 // PLT_UPnPMessageHelper::SetTimeOut(timeout);
445 // return NPT_SUCCESS;
446 // }
447 //
448 // reject if we have too many subscribers already
449 if (m_Subscribers.GetItemCount() > 30) {
450 response.SetStatus(500, "Internal Server Error");
451 return NPT_FAILURE;
452 }
453
454 //TODO: prevent hacking by making sure callbackurl is not ourselves?
455
456 // generate a unique subscriber ID
457 NPT_String sid;
458 PLT_UPnPMessageHelper::GenerateUUID(19, sid);
459
460 PLT_EventSubscriber* subscriber = new PLT_EventSubscriber(task_manager, this, sid, timeout);
461 // parse the callback URLs
462 bool reachable = false;
463 if (callback_urls[0] == '<') {
464 char* szURLs = (char*)(const char*)callback_urls;
465 char* brackL = szURLs;
466 char* brackR = szURLs;
467 while (++brackR < szURLs + callback_urls.GetLength()) {
468 if (*brackR == '>') {
469 NPT_String strCallbackURL = NPT_String(brackL+1, (NPT_Size)(brackR-brackL-1));
470 NPT_HttpUrl url(strCallbackURL);
471
472 if (url.IsValid()) {
473 subscriber->AddCallbackURL(strCallbackURL);
474 reachable = true;
475 }
476 brackL = ++brackR;
477 }
478 }
479 }
480
481 if (reachable == false) {
482 NPT_CHECK_LABEL_FATAL(NPT_FAILURE, cleanup);
483 }
484
485 // keep track of which interface we receive the request, we will use this one
486 // when notifying
487 subscriber->SetLocalIf(addr);
488
489 PLT_UPnPMessageHelper::SetSID(response, subscriber->GetSID());
490 PLT_UPnPMessageHelper::SetTimeOut(response, timeout);
491
492 {
493 NPT_AutoLock lock(m_Lock);
494
495 // new subscriber should get all vars in the LastChange var
496 UpdateLastChange(m_StateVars);
497
498 // send all state vars to sub
499 NPT_Result res = subscriber->Notify(m_StateVars);
500
501 // reset LastChange var to what was really just changed
502 UpdateLastChange(m_StateVarsChanged);
503
504 NPT_CHECK_LABEL_FATAL(res, cleanup);
505
506 if (!m_EventTask) {
507 m_EventTask = new PLT_ServiceEventTask(this);
508 task_manager->StartTask(m_EventTask);
509 }
510
511 m_Subscribers.Add(subscriber);
512 }
513
514 return NPT_SUCCESS;
515
516 cleanup:
517 response.SetStatus(412, "Precondition Failed");
518 delete subscriber;
519 return NPT_FAILURE;
520 }
521
522 /*----------------------------------------------------------------------
523 | PLT_Service::ProcessRenewSubscription
524 +---------------------------------------------------------------------*/
525 NPT_Result
526 PLT_Service::ProcessRenewSubscription(const NPT_SocketAddress& addr,
527 const NPT_String& sid,
528 int timeout,
529 NPT_HttpResponse& response)
530 {
531 NPT_AutoLock lock(m_Lock);
532
533 NPT_LOG_FINE_2("Renewing subscription for %s (sub=%s)",
534 m_EventSubURL.GetChars(),
535 sid.GetChars());
536
537 // first look if we don't have a subscriber with same callbackURL
538 PLT_EventSubscriber* subscriber = NULL;
539 if (NPT_SUCCEEDED(NPT_ContainerFind(m_Subscribers,
540 PLT_EventSubscriberFinderBySID(sid),
541 subscriber))) {
542 // update local interface and timeout
543 subscriber->SetLocalIf(addr);
544 subscriber->SetTimeout(timeout);
545
546 PLT_UPnPMessageHelper::SetSID(response, subscriber->GetSID());
547 PLT_UPnPMessageHelper::SetTimeOut(response, timeout);
548 return NPT_SUCCESS;
549 }
550
551 NPT_LOG_WARNING_1("Renewing subscription for unknown %s!", sid.GetChars());
552
553 // didn't find a valid Subscriber in our list
554 response.SetStatus(412, "Precondition Failed");
555 return NPT_FAILURE;
556 }
557
558 /*----------------------------------------------------------------------
559 | PLT_Service::ProcessCancelSubscription
560 +---------------------------------------------------------------------*/
561 NPT_Result
562 PLT_Service::ProcessCancelSubscription(const NPT_SocketAddress& /* addr */,
563 const NPT_String& sid,
564 NPT_HttpResponse& response)
565 {
566 NPT_AutoLock lock(m_Lock);
567
568 NPT_LOG_FINE_2("Cancelling subscription for %s (sub=%s)",
569 m_EventSubURL.GetChars(),
570 sid.GetChars());
571
572 // first look if we don't have a subscriber with same callbackURL
573 PLT_EventSubscriber* sub = NULL;
574 if (NPT_SUCCEEDED(NPT_ContainerFind(m_Subscribers,
575 PLT_EventSubscriberFinderBySID(sid),
576 sub))) {
577
578 // remove sub
579 m_Subscribers.Remove(sub);
580 delete sub;
581 return NPT_SUCCESS;
582 }
583
584 NPT_LOG_WARNING_1("Cancelling subscription for unknown %s!", sid.GetChars());
585
586 // didn't find a valid Subscriber in our list
587 response.SetStatus(412, "Precondition Failed");
588 return NPT_FAILURE;
589 }
590
591 /*----------------------------------------------------------------------
592 | PLT_Service::AddChanged
593 +---------------------------------------------------------------------*/
594 NPT_Result
595 PLT_Service::AddChanged(PLT_StateVariable* var)
596 {
597 NPT_AutoLock lock(m_Lock);
598
599 // no event task means no subscribers yet, so don't bother
600 // Note: this will take care also when setting default state
601 // variables values during init and avoid being published
602 if (!m_EventTask) return NPT_SUCCESS;
603
604 if (var->IsSendingEvents()) {
605 if (!m_StateVarsToPublish.Contains(var))
606 m_StateVarsToPublish.Add(var);
607 } else if (var->IsSendingEvents(true)) {
608 if (!m_StateVarsChanged.Contains(var))
609 m_StateVarsChanged.Add(var);
610 UpdateLastChange(m_StateVarsChanged);
611 }
612
613 return NPT_SUCCESS;
614 }
615
616 /*----------------------------------------------------------------------
617 | PLT_Service::UpdateLastChange
618 +---------------------------------------------------------------------*/
619 NPT_Result
620 PLT_Service::UpdateLastChange(NPT_List<PLT_StateVariable*>& vars)
621 {
622 PLT_StateVariable* var = FindStateVariable("LastChange");
623 if (var == NULL) return NPT_FAILURE;
624
625 NPT_ASSERT(m_LastChangeNamespace.GetLength() > 0);
626
627 if (vars.GetItemCount() == 0) {
628 // no vars to update, remove LastChange from vars to publish
629 m_StateVarsToPublish.Remove(var);
630 return NPT_SUCCESS;
631 }
632
633 NPT_XmlElementNode* top = new NPT_XmlElementNode("Event");
634 NPT_CHECK_SEVERE(top->SetNamespaceUri("", m_LastChangeNamespace));
635
636 NPT_XmlElementNode* instance = new NPT_XmlElementNode("InstanceID");
637 NPT_CHECK_SEVERE(top->AddChild(instance));
638 NPT_CHECK_SEVERE(instance->SetAttribute("val", "0"));
639
640 // build list of changes
641 NPT_CHECK_SEVERE(vars.Apply(PLT_LastChangeXMLIterator(instance)));
642
643 // serialize node
644 NPT_String value;
645 NPT_CHECK_SEVERE(PLT_XmlHelper::Serialize(*top, value));
646 delete top;
647
648 // set the state change direcly instead of calling SetValue
649 // to avoid recursive lock, instead add var to publish here directly
650 var->m_Value = value;
651 if (!m_StateVarsToPublish.Contains(var)) m_StateVarsToPublish.Add(var);
652 return NPT_SUCCESS;
653 }
654
655 /*----------------------------------------------------------------------
656 | PLT_Service::PauseEventing
657 +---------------------------------------------------------------------*/
658 NPT_Result
659 PLT_Service::PauseEventing(bool paused)
660 {
661 NPT_AutoLock lock(m_Lock);
662 m_EventingPaused = paused;
663 return NPT_SUCCESS;
664 }
665
666 /*----------------------------------------------------------------------
667 | PLT_Service::NotifyChanged
668 +---------------------------------------------------------------------*/
669 NPT_Result
670 PLT_Service::NotifyChanged()
671 {
672 NPT_AutoLock lock(m_Lock);
673
674 // no eventing for now
675 if (m_EventingPaused) return NPT_SUCCESS;
676
677 // pick the vars that are ready to be published
678 // based on their moderation rate and last publication
679 NPT_List<PLT_StateVariable*> vars_ready;
680 NPT_List<PLT_StateVariable*>::Iterator iter = m_StateVarsToPublish.GetFirstItem();
681 while (iter) {
682 PLT_StateVariable* var = *iter;
683 if (var->IsReadyToPublish()) {
684 vars_ready.Add(var);
685 m_StateVarsToPublish.Erase(iter++);
686
687 // clear last changed list if we're about to send LastChange var
688 if (!var->GetName().Compare("LastChange")) m_StateVarsChanged.Clear();
689
690 continue;
691 }
692
693 iter++;
694 }
695
696 // if nothing to publish then bail out now
697 // we'll clean up expired subscribers when we have something to publish
698 if (vars_ready.GetItemCount() == 0) return NPT_SUCCESS;
699
700 // send vars that are ready to go and remove old subscribers
701 NPT_List<PLT_EventSubscriber*>::Iterator sub_iter = m_Subscribers.GetFirstItem();
702 while (sub_iter) {
703 PLT_EventSubscriber* sub = *sub_iter;
704
705 NPT_TimeStamp now, expiration;
706 NPT_System::GetCurrentTimeStamp(now);
707 expiration = sub->GetExpirationTime();
708
709 // forget sub if it didn't renew in time or if notification failed
710 if (expiration == NPT_TimeStamp() || expiration > now ) {
711 NPT_Result res = vars_ready.GetItemCount()?sub->Notify(vars_ready):NPT_SUCCESS;
712 if (NPT_SUCCEEDED(res)) {
713 sub_iter++;
714 continue;
715 }
716 }
717
718 m_Subscribers.Erase(sub_iter++);
719 delete sub;
720 }
721
722 return NPT_SUCCESS;
723 }
724
725 /*----------------------------------------------------------------------
726 | PLT_ServiceSCPDURLFinder::operator()
727 +---------------------------------------------------------------------*/
728 bool
729 PLT_ServiceSCPDURLFinder::operator()(PLT_Service* const & service) const
730 {
731 NPT_String url = service->GetSCPDURL();
732 if (!url.StartsWith("/")) {
733 url = service->GetDevice()->GetURLBase().GetPath() + url;
734 }
735 return m_URL.Compare(url, true) ? false : true;
736 }
737
738 /*----------------------------------------------------------------------
739 | PLT_ServiceControlURLFinder::operator()
740 +---------------------------------------------------------------------*/
741 bool
742 PLT_ServiceControlURLFinder::operator()(PLT_Service* const & service) const
743 {
744 NPT_String url = service->GetControlURL();
745 if (!url.StartsWith("/")) {
746 url = service->GetDevice()->GetURLBase().GetPath() + url;
747 }
748 return m_URL.Compare(url, true) ? false : true;
749 }
750
751 /*----------------------------------------------------------------------
752 | PLT_ServiceEventSubURLFinder::operator()
753 +---------------------------------------------------------------------*/
754 bool
755 PLT_ServiceEventSubURLFinder::operator()(PLT_Service* const & service) const
756 {
757 NPT_String url = service->GetEventSubURL();
758 if (!url.StartsWith("/")) {
759 url = service->GetDevice()->GetURLBase().GetPath() + url;
760 }
761 return m_URL.Compare(url, true) ? false : true;
762 }
763
764 /*----------------------------------------------------------------------
765 | PLT_ServiceIDFinder::operator()
766 +---------------------------------------------------------------------*/
767 bool
768 PLT_ServiceIDFinder::operator()(PLT_Service* const & service) const
769 {
770 return m_Id.Compare(service->GetServiceID(), true) ? false : true;
771 }
772
773 /*----------------------------------------------------------------------
774 | PLT_ServiceTypeFinder::operator()
775 +---------------------------------------------------------------------*/
776 bool
777 PLT_ServiceTypeFinder::operator()(PLT_Service* const & service) const
778 {
779 return m_Type.Compare(service->GetServiceType(), true) ? false : true;
780 }
781
782 /*----------------------------------------------------------------------
783 | PLT_GetLastChangeXMLIterator::operator()
784 +---------------------------------------------------------------------*/
785 NPT_Result
786 PLT_LastChangeXMLIterator::operator()(PLT_StateVariable* const &var) const
787 {
788 // only add vars that are indirectly evented
789 if (!var->IsSendingEvents(true)) return NPT_SUCCESS;
790
791 NPT_XmlElementNode* variable = new NPT_XmlElementNode((const char*)var->GetName());
792 NPT_CHECK_SEVERE(m_Node->AddChild(variable));
793 NPT_CHECK_SEVERE(variable->SetAttribute("val", var->GetValue()));
794 return NPT_SUCCESS;
795 }