Mercurial > projects > hoofbaby
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 } |