1 /*****************************************************************
2 |
3 | Platinum - Control Point
4 |
5 | Copyright (c) 2004-2010, 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 | licensing@plutinosoft.com
21 |
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.
26 |
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
32 |
33 ****************************************************************/
34
35 /*----------------------------------------------------------------------
36 | includes
37 +---------------------------------------------------------------------*/
38 #include "PltCtrlPoint.h"
39 #include "PltUPnP.h"
40 #include "PltDeviceData.h"
41 #include "PltUtilities.h"
42 #include "PltCtrlPointTask.h"
43 #include "PltSsdp.h"
44 #include "PltHttpServer.h"
45 #include "PltConstants.h"
46
47 NPT_SET_LOCAL_LOGGER("platinum.core.ctrlpoint")
48
49 /*----------------------------------------------------------------------
50 | PLT_CtrlPointListenerOnDeviceAddedIterator class
51 +---------------------------------------------------------------------*/
52 class PLT_CtrlPointListenerOnDeviceAddedIterator
53 {
54 public:
PLT_CtrlPointListenerOnDeviceAddedIterator(PLT_DeviceDataReference & device)55 PLT_CtrlPointListenerOnDeviceAddedIterator(PLT_DeviceDataReference& device) :
56 m_Device(device) {}
57
operator ()(PLT_CtrlPointListener * & listener) const58 NPT_Result operator()(PLT_CtrlPointListener*& listener) const {
59 return listener->OnDeviceAdded(m_Device);
60 }
61
62 private:
63 PLT_DeviceDataReference& m_Device;
64 };
65
66 /*----------------------------------------------------------------------
67 | PLT_CtrlPointListenerOnDeviceRemovedIterator class
68 +---------------------------------------------------------------------*/
69 class PLT_CtrlPointListenerOnDeviceRemovedIterator
70 {
71 public:
PLT_CtrlPointListenerOnDeviceRemovedIterator(PLT_DeviceDataReference & device)72 PLT_CtrlPointListenerOnDeviceRemovedIterator(PLT_DeviceDataReference& device) :
73 m_Device(device) {}
74
operator ()(PLT_CtrlPointListener * & listener) const75 NPT_Result operator()(PLT_CtrlPointListener*& listener) const {
76 return listener->OnDeviceRemoved(m_Device);
77 }
78
79 private:
80 PLT_DeviceDataReference& m_Device;
81 };
82
83 /*----------------------------------------------------------------------
84 | PLT_CtrlPointListenerOnActionResponseIterator class
85 +---------------------------------------------------------------------*/
86 class PLT_CtrlPointListenerOnActionResponseIterator
87 {
88 public:
PLT_CtrlPointListenerOnActionResponseIterator(NPT_Result res,PLT_ActionReference & action,void * userdata)89 PLT_CtrlPointListenerOnActionResponseIterator(NPT_Result res,
90 PLT_ActionReference& action,
91 void* userdata) :
92 m_Res(res), m_Action(action), m_Userdata(userdata) {}
93
operator ()(PLT_CtrlPointListener * & listener) const94 NPT_Result operator()(PLT_CtrlPointListener*& listener) const {
95 return listener->OnActionResponse(m_Res, m_Action, m_Userdata);
96 }
97
98 private:
99 NPT_Result m_Res;
100 PLT_ActionReference& m_Action;
101 void* m_Userdata;
102 };
103
104 /*----------------------------------------------------------------------
105 | PLT_CtrlPointListenerOnEventNotifyIterator class
106 +---------------------------------------------------------------------*/
107 class PLT_CtrlPointListenerOnEventNotifyIterator
108 {
109 public:
PLT_CtrlPointListenerOnEventNotifyIterator(PLT_Service * service,NPT_List<PLT_StateVariable * > * vars)110 PLT_CtrlPointListenerOnEventNotifyIterator(PLT_Service* service,
111 NPT_List<PLT_StateVariable*>* vars) :
112 m_Service(service), m_Vars(vars) {}
113
operator ()(PLT_CtrlPointListener * & listener) const114 NPT_Result operator()(PLT_CtrlPointListener*& listener) const {
115 return listener->OnEventNotify(m_Service, m_Vars);
116 }
117
118 private:
119 PLT_Service* m_Service;
120 NPT_List<PLT_StateVariable*>* m_Vars;
121 };
122
123 /*----------------------------------------------------------------------
124 | PLT_AddGetSCPDRequestIterator class
125 +---------------------------------------------------------------------*/
126 class PLT_AddGetSCPDRequestIterator
127 {
128 public:
PLT_AddGetSCPDRequestIterator(PLT_CtrlPointGetSCPDsTask & task,PLT_DeviceDataReference & device)129 PLT_AddGetSCPDRequestIterator(PLT_CtrlPointGetSCPDsTask& task,
130 PLT_DeviceDataReference& device) :
131 m_Task(task), m_Device(device) {}
132
operator ()(PLT_Service * & service) const133 NPT_Result operator()(PLT_Service*& service) const {
134 // look for the host and port of the device
135 NPT_String scpd_url = service->GetSCPDURL(true);
136
137 NPT_LOG_FINER_3("Queueing SCPD request for service \"%s\" of device \"%s\" @ %s",
138 (const char*)service->GetServiceID(),
139 (const char*)service->GetDevice()->GetFriendlyName(),
140 (const char*)scpd_url);
141
142 // verify url before queuing just in case
143 NPT_HttpUrl url(scpd_url);
144 if (!url.IsValid()) {
145 NPT_LOG_SEVERE_3("Invalid SCPD url \"%s\" for service \"%s\" of device \"%s\"!",
146 (const char*)scpd_url,
147 (const char*)service->GetServiceID(),
148 (const char*)service->GetDevice()->GetFriendlyName());
149 return NPT_ERROR_INVALID_SYNTAX;
150 }
151
152 // Create request and attach service to it
153 PLT_CtrlPointGetSCPDRequest* request =
154 new PLT_CtrlPointGetSCPDRequest((PLT_DeviceDataReference&)m_Device, scpd_url, "GET", NPT_HTTP_PROTOCOL_1_1);
155 return m_Task.AddSCPDRequest(request);
156 }
157
158 private:
159 PLT_CtrlPointGetSCPDsTask& m_Task;
160 PLT_DeviceDataReference m_Device;
161 };
162
163 /*----------------------------------------------------------------------
164 | PLT_EventSubscriberRemoverIterator class
165 +---------------------------------------------------------------------*/
166 // Note: The PLT_CtrlPoint::m_Lock must be acquired prior to using any
167 // function such as Apply on this iterator
168 class PLT_EventSubscriberRemoverIterator
169 {
170 public:
PLT_EventSubscriberRemoverIterator(PLT_CtrlPoint * ctrl_point)171 PLT_EventSubscriberRemoverIterator(PLT_CtrlPoint* ctrl_point) :
172 m_CtrlPoint(ctrl_point) {}
~PLT_EventSubscriberRemoverIterator()173 ~PLT_EventSubscriberRemoverIterator() {}
174
operator ()(PLT_Service * & service) const175 NPT_Result operator()(PLT_Service*& service) const {
176 PLT_EventSubscriberReference sub;
177 if (NPT_SUCCEEDED(NPT_ContainerFind(m_CtrlPoint->m_Subscribers,
178 PLT_EventSubscriberFinderByService(service), sub))) {
179 NPT_LOG_INFO_1("Removed subscriber \"%s\"", (const char*)sub->GetSID());
180 m_CtrlPoint->m_Subscribers.Remove(sub);
181 }
182
183 return NPT_SUCCESS;
184 }
185
186 private:
187 PLT_CtrlPoint* m_CtrlPoint;
188 };
189
190 /*----------------------------------------------------------------------
191 | PLT_ServiceReadyIterator class
192 +---------------------------------------------------------------------*/
193 class PLT_ServiceReadyIterator
194 {
195 public:
PLT_ServiceReadyIterator()196 PLT_ServiceReadyIterator() {}
197
operator ()(PLT_Service * & service) const198 NPT_Result operator()(PLT_Service*& service) const {
199 return service->IsValid()?NPT_SUCCESS:NPT_FAILURE;
200 }
201 };
202
203 /*----------------------------------------------------------------------
204 | PLT_DeviceReadyIterator class
205 +---------------------------------------------------------------------*/
206 class PLT_DeviceReadyIterator
207 {
208 public:
PLT_DeviceReadyIterator()209 PLT_DeviceReadyIterator() {}
operator ()(PLT_DeviceDataReference & device) const210 NPT_Result operator()(PLT_DeviceDataReference& device) const {
211 NPT_Result res = device->m_Services.ApplyUntil(
212 PLT_ServiceReadyIterator(),
213 NPT_UntilResultNotEquals(NPT_SUCCESS));
214 if (NPT_FAILED(res)) return res;
215
216 res = device->m_EmbeddedDevices.ApplyUntil(
217 PLT_DeviceReadyIterator(),
218 NPT_UntilResultNotEquals(NPT_SUCCESS));
219 if (NPT_FAILED(res)) return res;
220
221 // a device must have at least one service or embedded device
222 // otherwise it's not ready
223 if (device->m_Services.GetItemCount() == 0 &&
224 device->m_EmbeddedDevices.GetItemCount() == 0) {
225 return NPT_FAILURE;
226 }
227
228 return NPT_SUCCESS;
229 }
230 };
231
232 /*----------------------------------------------------------------------
233 | PLT_CtrlPoint::PLT_CtrlPoint
234 +---------------------------------------------------------------------*/
PLT_CtrlPoint(const char * search_criteria)235 PLT_CtrlPoint::PLT_CtrlPoint(const char* search_criteria /* = "upnp:rootdevice" */) :
236 m_EventHttpServer(NULL),
237 m_TaskManager(NULL),
238 m_Lock(true),
239 m_SearchCriteria(search_criteria),
240 m_Started(false)
241 {
242 }
243
244 /*----------------------------------------------------------------------
245 | PLT_CtrlPoint::~PLT_CtrlPoint
246 +---------------------------------------------------------------------*/
~PLT_CtrlPoint()247 PLT_CtrlPoint::~PLT_CtrlPoint()
248 {
249 }
250
251 /*----------------------------------------------------------------------
252 | PLT_CtrlPoint::IgnoreUUID
253 +---------------------------------------------------------------------*/
254 void
IgnoreUUID(const char * uuid)255 PLT_CtrlPoint::IgnoreUUID(const char* uuid)
256 {
257 if (!m_UUIDsToIgnore.Find(NPT_StringFinder(uuid))) {
258 m_UUIDsToIgnore.Add(uuid);
259 }
260 }
261
262 /*----------------------------------------------------------------------
263 | PLT_CtrlPoint::Start
264 +---------------------------------------------------------------------*/
265 NPT_Result
Start(PLT_SsdpListenTask * task)266 PLT_CtrlPoint::Start(PLT_SsdpListenTask* task)
267 {
268 if (m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
269
270 m_TaskManager = new PLT_TaskManager();
271
272 m_EventHttpServer = new PLT_HttpServer();
273 m_EventHttpServer->AddRequestHandler(new PLT_HttpRequestHandler(this), "/", true, true);
274 m_EventHttpServer->Start();
275
276 // house keeping task
277 m_TaskManager->StartTask(new PLT_CtrlPointHouseKeepingTask(this));
278
279 // add ourselves as an listener to SSDP multicast advertisements
280 task->AddListener(this);
281
282 //
283 // use next line instead for DLNA testing, faster frequency for M-SEARCH
284 //return m_SearchCriteria.GetLength()?Search(NPT_HttpUrl("239.255.255.250", 1900, "*"), m_SearchCriteria, 1, 5000):NPT_SUCCESS;
285 //
286
287 m_Started = true;
288
289 return m_SearchCriteria.GetLength()?Search(NPT_HttpUrl("239.255.255.250", 1900, "*"), m_SearchCriteria):NPT_SUCCESS;
290 }
291
292 /*----------------------------------------------------------------------
293 | PLT_CtrlPoint::GetPort
294 +---------------------------------------------------------------------*/
295 NPT_Result
GetPort(NPT_UInt16 & port)296 PLT_CtrlPoint::GetPort(NPT_UInt16& port)
297 {
298 if (!m_Started) return NPT_ERROR_INVALID_STATE;
299
300 port = m_EventHttpServer->GetPort();
301 return NPT_SUCCESS;
302 }
303
304 /*----------------------------------------------------------------------
305 | PLT_CtrlPoint::Stop
306 +---------------------------------------------------------------------*/
307 NPT_Result
Stop(PLT_SsdpListenTask * task)308 PLT_CtrlPoint::Stop(PLT_SsdpListenTask* task)
309 {
310 if (!m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
311
312 m_Started = false;
313
314 task->RemoveListener(this);
315
316 m_EventHttpServer->Stop();
317 m_TaskManager->Abort();
318
319 // force remove all devices
320 NPT_List<PLT_DeviceDataReference>::Iterator iter = m_RootDevices.GetFirstItem();
321 while (iter) {
322 NotifyDeviceRemoved(*iter);
323 ++iter;
324 }
325
326 // we can safely clear everything without a lock
327 // as there are no more tasks pending
328 m_RootDevices.Clear();
329 m_Subscribers.Clear();
330
331 m_EventHttpServer = NULL;
332 m_TaskManager = NULL;
333
334 return NPT_SUCCESS;
335 }
336
337 /*----------------------------------------------------------------------
338 | PLT_CtrlPoint::AddListener
339 +---------------------------------------------------------------------*/
340 NPT_Result
AddListener(PLT_CtrlPointListener * listener)341 PLT_CtrlPoint::AddListener(PLT_CtrlPointListener* listener)
342 {
343 NPT_AutoLock lock(m_Lock);
344 if (!m_ListenerList.Contains(listener)) {
345 m_ListenerList.Add(listener);
346 }
347 return NPT_SUCCESS;
348 }
349
350 /*----------------------------------------------------------------------
351 | PLT_CtrlPoint::RemoveListener
352 +---------------------------------------------------------------------*/
353 NPT_Result
RemoveListener(PLT_CtrlPointListener * listener)354 PLT_CtrlPoint::RemoveListener(PLT_CtrlPointListener* listener)
355 {
356 NPT_AutoLock lock(m_Lock);
357 m_ListenerList.Remove(listener);
358 return NPT_SUCCESS;
359 }
360
361 /*----------------------------------------------------------------------
362 | PLT_CtrlPoint::CreateSearchTask
363 +---------------------------------------------------------------------*/
364 PLT_SsdpSearchTask*
CreateSearchTask(const NPT_HttpUrl & url,const char * target,NPT_Cardinal mx,NPT_TimeInterval frequency,const NPT_IpAddress & address)365 PLT_CtrlPoint::CreateSearchTask(const NPT_HttpUrl& url,
366 const char* target,
367 NPT_Cardinal mx,
368 NPT_TimeInterval frequency,
369 const NPT_IpAddress& address)
370 {
371 // make sure mx is at least 1
372 if (mx<1) mx=1;
373
374 // create socket
375 NPT_Reference<NPT_UdpMulticastSocket> socket(new NPT_UdpMulticastSocket(NPT_SOCKET_FLAG_CANCELLABLE));
376 socket->SetInterface(address);
377 socket->SetTimeToLive(PLT_Constants::GetInstance().GetSearchMulticastTimeToLive());
378
379 // bind to something > 1024 and different than 1900
380 int retries = 20;
381 do {
382 int random = NPT_System::GetRandomInteger();
383 int port = (unsigned short)(1024 + (random % 15000));
384 if (port == 1900) continue;
385
386 if (NPT_SUCCEEDED(socket->Bind(
387 NPT_SocketAddress(NPT_IpAddress::Any, port),
388 false)))
389 break;
390
391 } while (--retries);
392
393 if (retries == 0) {
394 NPT_LOG_SEVERE("Couldn't bind socket for Search Task");
395 return NULL;
396 }
397
398 // create request
399 NPT_HttpRequest* request = new NPT_HttpRequest(url, "M-SEARCH", NPT_HTTP_PROTOCOL_1_1);
400 PLT_UPnPMessageHelper::SetMX(*request, mx);
401 PLT_UPnPMessageHelper::SetST(*request, target);
402 PLT_UPnPMessageHelper::SetMAN(*request, "\"ssdp:discover\"");
403 request->GetHeaders().SetHeader(NPT_HTTP_HEADER_USER_AGENT, *PLT_Constants::GetInstance().GetDefaultUserAgent());
404
405 // create task
406 PLT_SsdpSearchTask* task = new PLT_SsdpSearchTask(
407 socket.AsPointer(),
408 this,
409 request,
410 (frequency.ToMillis()>0 && frequency.ToMillis()<5000)?NPT_TimeInterval(5.):frequency); /* repeat no less than every 5 secs */
411 socket.Detach();
412
413 return task;
414 }
415
416 /*----------------------------------------------------------------------
417 | PLT_CtrlPoint::Search
418 +---------------------------------------------------------------------*/
419 NPT_Result
Search(const NPT_HttpUrl & url,const char * target,NPT_Cardinal mx,NPT_TimeInterval frequency,NPT_TimeInterval initial_delay)420 PLT_CtrlPoint::Search(const NPT_HttpUrl& url,
421 const char* target,
422 NPT_Cardinal mx /* = 5 */,
423 NPT_TimeInterval frequency /* = NPT_TimeInterval(50.) */,
424 NPT_TimeInterval initial_delay /* = NPT_TimeInterval(0.) */)
425 {
426 if (!m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
427
428 NPT_List<NPT_NetworkInterface*> if_list;
429 NPT_List<NPT_NetworkInterface*>::Iterator net_if;
430 NPT_List<NPT_NetworkInterfaceAddress>::Iterator net_if_addr;
431
432 NPT_CHECK_SEVERE(PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list, true));
433
434 for (net_if = if_list.GetFirstItem();
435 net_if;
436 net_if++) {
437 // make sure the interface is at least broadcast or multicast
438 if (!((*net_if)->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_MULTICAST) &&
439 !((*net_if)->GetFlags() & NPT_NETWORK_INTERFACE_FLAG_BROADCAST)) {
440 continue;
441 }
442
443 for (net_if_addr = (*net_if)->GetAddresses().GetFirstItem();
444 net_if_addr;
445 net_if_addr++) {
446 // create task
447 PLT_SsdpSearchTask* task = CreateSearchTask(url,
448 target,
449 mx,
450 frequency,
451 (*net_if_addr).GetPrimaryAddress());
452 m_TaskManager->StartTask(task, &initial_delay);
453 }
454 }
455
456 if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
457 return NPT_SUCCESS;
458 }
459
460 /*----------------------------------------------------------------------
461 | PLT_CtrlPoint::Discover
462 +---------------------------------------------------------------------*/
463 NPT_Result
Discover(const NPT_HttpUrl & url,const char * target,NPT_Cardinal mx,NPT_TimeInterval frequency,NPT_TimeInterval initial_delay)464 PLT_CtrlPoint::Discover(const NPT_HttpUrl& url,
465 const char* target,
466 NPT_Cardinal mx, /* = 5 */
467 NPT_TimeInterval frequency /* = NPT_TimeInterval(50.) */,
468 NPT_TimeInterval initial_delay /* = NPT_TimeInterval(0.) */)
469 {
470 if (!m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
471
472 // make sure mx is at least 1
473 if (mx<1) mx = 1;
474
475 // create socket
476 NPT_UdpSocket* socket = new NPT_UdpSocket(NPT_SOCKET_FLAG_CANCELLABLE);
477
478 // create request
479 NPT_HttpRequest* request = new NPT_HttpRequest(url, "M-SEARCH", NPT_HTTP_PROTOCOL_1_1);
480 PLT_UPnPMessageHelper::SetMX(*request, mx);
481 PLT_UPnPMessageHelper::SetST(*request, target);
482 PLT_UPnPMessageHelper::SetMAN(*request, "\"ssdp:discover\"");
483 request->GetHeaders().SetHeader(NPT_HTTP_HEADER_USER_AGENT, *PLT_Constants::GetInstance().GetDefaultUserAgent());
484
485 // force HOST to be the regular multicast address:port
486 // Some servers do care (like WMC) otherwise they won't respond to us
487 request->GetHeaders().SetHeader(NPT_HTTP_HEADER_HOST, "239.255.255.250:1900");
488
489 // create task
490 PLT_ThreadTask* task = new PLT_SsdpSearchTask(
491 socket,
492 this,
493 request,
494 (frequency.ToMillis()>0 && frequency.ToMillis()<5000)?NPT_TimeInterval(5.):frequency); /* repeat no less than every 5 secs */
495 return m_TaskManager->StartTask(task, &initial_delay);
496 }
497
498 /*----------------------------------------------------------------------
499 | PLT_CtrlPoint::DoHouseKeeping
500 +---------------------------------------------------------------------*/
501 NPT_Result
DoHouseKeeping()502 PLT_CtrlPoint::DoHouseKeeping()
503 {
504 NPT_List<PLT_DeviceDataReference> devices_to_remove;
505
506 // remove expired devices
507 {
508 NPT_AutoLock lock(m_Lock);
509
510 PLT_DeviceDataReference head, device;
511 while (NPT_SUCCEEDED(m_RootDevices.PopHead(device))) {
512 NPT_TimeStamp last_update = device->GetLeaseTimeLastUpdate();
513 NPT_TimeInterval lease_time = device->GetLeaseTime();
514
515 // check if device lease time has expired or if failed to renew subscribers
516 // TODO: UDA 1.1 says that root device and all embedded devices must have expired
517 // before we can assume they're all no longer unavailable (we may have missed the root device renew)
518 NPT_TimeStamp now;
519 NPT_System::GetCurrentTimeStamp(now);
520 if (now > last_update + NPT_TimeInterval((double)lease_time*2)) {
521 devices_to_remove.Add(device);
522 } else {
523 // add the device back to our list since it is still alive
524 m_RootDevices.Add(device);
525
526 // keep track of first device added back to list
527 // to know we checked all devices in initial list
528 if (head.IsNull()) head = device;
529 }
530
531 // have we exhausted initial list?
532 if (!head.IsNull() && head == *m_RootDevices.GetFirstItem())
533 break;
534 };
535 }
536
537 // remove old devices
538 {
539 NPT_AutoLock lock(m_Lock);
540
541 for (NPT_List<PLT_DeviceDataReference>::Iterator device =
542 devices_to_remove.GetFirstItem();
543 device;
544 device++) {
545 RemoveDevice(*device);
546 }
547 }
548
549 // renew subscribers of subscribed device services
550 NPT_List<PLT_ThreadTask*> tasks;
551 {
552 NPT_AutoLock lock(m_Lock);
553
554 NPT_List<PLT_EventSubscriberReference>::Iterator sub = m_Subscribers.GetFirstItem();
555 while (sub) {
556 NPT_TimeStamp now;
557 NPT_System::GetCurrentTimeStamp(now);
558
559 // time to renew if within 90 secs of expiration
560 if (now > (*sub)->GetExpirationTime() - NPT_TimeStamp(90.)) {
561 PLT_ThreadTask* task = RenewSubscriber(*sub);
562 if (task) tasks.Add(task);
563 }
564 sub++;
565 }
566 }
567
568 // Queue up all tasks now outside of lock, in case they
569 // block because the task manager has maxed out number of running tasks
570 // and to avoid a deadlock with tasks trying to acquire the lock in the response
571 NPT_List<PLT_ThreadTask*>::Iterator task = tasks.GetFirstItem();
572 while (task) {
573 PLT_ThreadTask* _task = *task++;
574 m_TaskManager->StartTask(_task);
575 }
576
577 return NPT_SUCCESS;
578 }
579
580 /*----------------------------------------------------------------------
581 | PLT_CtrlPoint::FindDevice
582 +---------------------------------------------------------------------*/
583 NPT_Result
FindDevice(const char * uuid,PLT_DeviceDataReference & device,bool return_root)584 PLT_CtrlPoint::FindDevice(const char* uuid,
585 PLT_DeviceDataReference& device,
586 bool return_root /* = false */)
587 {
588 NPT_List<PLT_DeviceDataReference>::Iterator iter = m_RootDevices.GetFirstItem();
589 while (iter) {
590 // device uuid found immediately as root device
591 if ((*iter)->GetUUID().Compare(uuid) == 0) {
592 device = *iter;
593 return NPT_SUCCESS;
594 } else if (NPT_SUCCEEDED((*iter)->FindEmbeddedDevice(uuid, device))) {
595 // we found the uuid as an embedded device of this root
596 // return root if told, otherwise return found embedded device
597 if (return_root) device = (*iter);
598 return NPT_SUCCESS;
599 }
600 ++iter;
601 }
602
603 return NPT_ERROR_NO_SUCH_ITEM;
604 }
605
606 /*----------------------------------------------------------------------
607 | PLT_CtrlPoint::FindActionDesc
608 +---------------------------------------------------------------------*/
609 NPT_Result
FindActionDesc(PLT_DeviceDataReference & device,const char * service_type,const char * action_name,PLT_ActionDesc * & action_desc)610 PLT_CtrlPoint::FindActionDesc(PLT_DeviceDataReference& device,
611 const char* service_type,
612 const char* action_name,
613 PLT_ActionDesc*& action_desc)
614 {
615 if (device.IsNull()) return NPT_ERROR_INVALID_PARAMETERS;
616
617 // look for the service
618 PLT_Service* service;
619 if (NPT_FAILED(device->FindServiceByType(service_type, service))) {
620 NPT_LOG_FINE_1("Service %s not found", (const char*)service_type);
621 return NPT_FAILURE;
622 }
623
624 action_desc = service->FindActionDesc(action_name);
625 if (action_desc == NULL) {
626 NPT_LOG_FINE_1("Action %s not found in service", action_name);
627 return NPT_FAILURE;
628 }
629
630 return NPT_SUCCESS;
631 }
632
633 /*----------------------------------------------------------------------
634 | PLT_CtrlPoint::CreateAction
635 +---------------------------------------------------------------------*/
636 NPT_Result
CreateAction(PLT_DeviceDataReference & device,const char * service_type,const char * action_name,PLT_ActionReference & action)637 PLT_CtrlPoint::CreateAction(PLT_DeviceDataReference& device,
638 const char* service_type,
639 const char* action_name,
640 PLT_ActionReference& action)
641 {
642 if (device.IsNull()) return NPT_ERROR_INVALID_PARAMETERS;
643
644 NPT_AutoLock lock(m_Lock);
645
646 PLT_ActionDesc* action_desc;
647 NPT_CHECK_SEVERE(FindActionDesc(device,
648 service_type,
649 action_name,
650 action_desc));
651
652 PLT_DeviceDataReference root_device;
653 NPT_CHECK_SEVERE(FindDevice(device->GetUUID(), root_device, true));
654
655 action = new PLT_Action(*action_desc, root_device);
656 return NPT_SUCCESS;
657 }
658
659 /*----------------------------------------------------------------------
660 | PLT_CtrlPoint::SetupResponse
661 +---------------------------------------------------------------------*/
662 NPT_Result
SetupResponse(NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)663 PLT_CtrlPoint::SetupResponse(NPT_HttpRequest& request,
664 const NPT_HttpRequestContext& context,
665 NPT_HttpResponse& response)
666 {
667 NPT_COMPILER_UNUSED(context);
668
669 if (request.GetMethod().Compare("NOTIFY") == 0) {
670 return ProcessHttpNotify(request, context, response);
671 }
672
673 NPT_LOG_SEVERE("CtrlPoint received bad http request\r\n");
674 response.SetStatus(412, "Precondition Failed");
675 return NPT_SUCCESS;
676 }
677
678 /*----------------------------------------------------------------------
679 | PLT_CtrlPoint::DecomposeLastChangeVar
680 +---------------------------------------------------------------------*/
681 NPT_Result
DecomposeLastChangeVar(NPT_List<PLT_StateVariable * > & vars)682 PLT_CtrlPoint::DecomposeLastChangeVar(NPT_List<PLT_StateVariable*>& vars)
683 {
684 // parse LastChange var into smaller vars
685 PLT_StateVariable* lastChangeVar = NULL;
686 if (NPT_SUCCEEDED(NPT_ContainerFind(vars,
687 PLT_StateVariableNameFinder("LastChange"),
688 lastChangeVar))) {
689 vars.Remove(lastChangeVar);
690 PLT_Service* var_service = lastChangeVar->GetService();
691 NPT_String text = lastChangeVar->GetValue();
692
693 NPT_XmlNode* xml = NULL;
694 NPT_XmlParser parser;
695 if (NPT_FAILED(parser.Parse(text, xml)) || !xml || !xml->AsElementNode()) {
696 delete xml;
697 return NPT_ERROR_INVALID_FORMAT;
698 }
699
700 NPT_XmlElementNode* node = xml->AsElementNode();
701 if (!node->GetTag().Compare("Event", true)) {
702 // look for the instance with attribute id = 0
703 NPT_XmlElementNode* instance = NULL;
704 for (NPT_Cardinal i=0; i<node->GetChildren().GetItemCount(); i++) {
705 NPT_XmlElementNode* child;
706 if (NPT_FAILED(PLT_XmlHelper::GetChild(node, child, i)))
707 continue;
708
709 if (!child->GetTag().Compare("InstanceID", true)) {
710 // extract the "val" attribute value
711 NPT_String value;
712 if (NPT_SUCCEEDED(PLT_XmlHelper::GetAttribute(child, "val", value)) &&
713 !value.Compare("0")) {
714 instance = child;
715 break;
716 }
717 }
718 }
719
720 // did we find an instance with id = 0 ?
721 if (instance != NULL) {
722 // all the children of the Instance node are state variables
723 for (NPT_Cardinal j=0; j<instance->GetChildren().GetItemCount(); j++) {
724 NPT_XmlElementNode* var_node;
725 if (NPT_FAILED(PLT_XmlHelper::GetChild(instance, var_node, j)))
726 continue;
727
728 // look for the state variable in this service
729 const NPT_String* value = var_node->GetAttribute("val");
730 PLT_StateVariable* var = var_service->FindStateVariable(var_node->GetTag());
731 if (value != NULL && var != NULL) {
732 // get the value and set the state variable
733 // if it succeeded, add it to the list of vars we'll event
734 if (NPT_SUCCEEDED(var->SetValue(*value))) {
735 vars.Add(var);
736 NPT_LOG_FINE_2("LastChange var change for (%s): %s",
737 (const char*)var->GetName(),
738 (const char*)var->GetValue());
739 }
740 }
741 }
742 }
743 }
744 delete xml;
745 }
746
747 return NPT_SUCCESS;
748 }
749
750 /*----------------------------------------------------------------------
751 | PLT_CtrlPoint::ProcessEventNotification
752 +---------------------------------------------------------------------*/
753 NPT_Result
ProcessEventNotification(PLT_EventSubscriberReference subscriber,PLT_EventNotification * notification,NPT_List<PLT_StateVariable * > & vars)754 PLT_CtrlPoint::ProcessEventNotification(PLT_EventSubscriberReference subscriber,
755 PLT_EventNotification* notification,
756 NPT_List<PLT_StateVariable*> &vars)
757 {
758 NPT_XmlElementNode* xml = NULL;
759 PLT_Service* service = subscriber->GetService();
760 PLT_DeviceData* device = service->GetDevice();
761
762 NPT_String uuid = device->GetUUID();
763 NPT_String service_id = service->GetServiceID();
764
765 // callback uri for this sub
766 NPT_String callback_uri = "/" + uuid + "/" + service_id;
767
768 if (notification->m_RequestUrl.GetPath().Compare(callback_uri, true)) {
769 NPT_CHECK_LABEL_WARNING(NPT_FAILURE, failure);
770 }
771
772 // if the sequence number is less than our current one, we got it out of order
773 // so we disregard it
774 if (subscriber->GetEventKey() && notification->m_EventKey < subscriber->GetEventKey()) {
775 NPT_CHECK_LABEL_WARNING(NPT_FAILURE, failure);
776 }
777
778 // parse body
779 if (NPT_FAILED(PLT_XmlHelper::Parse(notification->m_XmlBody, xml))) {
780 NPT_CHECK_LABEL_WARNING(NPT_FAILURE, failure);
781 }
782
783 // check envelope
784 if (xml->GetTag().Compare("propertyset", true)) {
785 NPT_CHECK_LABEL_WARNING(NPT_FAILURE, failure);
786 }
787
788 // check property set
789 // keep a vector of the state variables that changed
790 NPT_XmlElementNode* property;
791 PLT_StateVariable* var;
792 for (NPT_List<NPT_XmlNode*>::Iterator children = xml->GetChildren().GetFirstItem();
793 children;
794 children++) {
795 NPT_XmlElementNode* child = (*children)->AsElementNode();
796 if (!child) continue;
797
798 // check property
799 if (child->GetTag().Compare("property", true)) continue;
800
801 if (NPT_FAILED(PLT_XmlHelper::GetChild(child, property))) {
802 NPT_CHECK_LABEL_WARNING(NPT_FAILURE, failure);
803 }
804
805 var = service->FindStateVariable(property->GetTag());
806 if (var == NULL) continue;
807
808 if (NPT_FAILED(var->SetValue(property->GetText()?*property->GetText():""))) {
809 NPT_CHECK_LABEL_WARNING(NPT_FAILURE, failure);
810 }
811
812 vars.Add(var);
813 }
814
815 // update sequence
816 subscriber->SetEventKey(notification->m_EventKey);
817
818 // Look if a state variable LastChange was received and decompose it into
819 // independent state variable updates
820 DecomposeLastChangeVar(vars);
821
822 delete xml;
823 return NPT_SUCCESS;
824
825 failure:
826 NPT_LOG_SEVERE("CtrlPoint failed to process event notification");
827 delete xml;
828 return NPT_SUCCESS;
829 }
830
831 /*----------------------------------------------------------------------
832 | PLT_CtrlPoint::AddPendingEventNotification
833 +---------------------------------------------------------------------*/
834 NPT_Result
AddPendingEventNotification(PLT_EventNotification * notification)835 PLT_CtrlPoint::AddPendingEventNotification(PLT_EventNotification *notification)
836 {
837 // Only keep a maximum of 20 pending notifications
838 while (m_PendingNotifications.GetItemCount() > 20) {
839 PLT_EventNotification *garbage = NULL;
840 m_PendingNotifications.PopHead(garbage);
841 delete garbage;
842 }
843
844 m_PendingNotifications.Add(notification);
845 return NPT_SUCCESS;
846 }
847
848 /*----------------------------------------------------------------------
849 | PLT_CtrlPoint::ProcessPendingEventNotifications
850 +---------------------------------------------------------------------*/
851 NPT_Result
ProcessPendingEventNotifications()852 PLT_CtrlPoint::ProcessPendingEventNotifications()
853 {
854 NPT_Cardinal count = m_PendingNotifications.GetItemCount();
855 while (count--) {
856 NPT_List<PLT_StateVariable*> vars;
857 PLT_Service *service = NULL;
858 PLT_EventNotification *notification;
859
860 if (NPT_SUCCEEDED(m_PendingNotifications.PopHead(notification))) {
861 PLT_EventSubscriberReference sub;
862
863 // look for the subscriber with that sid
864 if (NPT_FAILED(NPT_ContainerFind(m_Subscribers,
865 PLT_EventSubscriberFinderBySID(notification->m_SID),
866 sub))) {
867 m_PendingNotifications.Add(notification);
868 continue;
869 }
870
871 // keep track of service for listeners later
872 service = sub->GetService();
873
874 // Reprocess notification
875 NPT_LOG_WARNING_1("Reprocessing delayed notification for subscriber %s", (const char*)notification->m_SID);
876 NPT_Result result = ProcessEventNotification(sub, notification, vars);
877 delete notification;
878
879 if (NPT_FAILED(result)) continue;
880 }
881
882 // notify listeners
883 if (service && vars.GetItemCount()) {
884 m_ListenerList.Apply(PLT_CtrlPointListenerOnEventNotifyIterator(service, &vars));
885 }
886 }
887
888 return NPT_SUCCESS;
889 }
890
891 /*----------------------------------------------------------------------
892 | PLT_CtrlPoint::ProcessHttpNotify
893 +---------------------------------------------------------------------*/
894 NPT_Result
ProcessHttpNotify(const NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)895 PLT_CtrlPoint::ProcessHttpNotify(const NPT_HttpRequest& request,
896 const NPT_HttpRequestContext& context,
897 NPT_HttpResponse& response)
898 {
899 NPT_COMPILER_UNUSED(context);
900
901 NPT_AutoLock lock(m_Lock);
902
903 NPT_List<PLT_StateVariable*> vars;
904 PLT_Service* service = NULL;
905 PLT_EventSubscriberReference sub;
906 NPT_Result result;
907
908 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINER, "PLT_CtrlPoint::ProcessHttpNotify:", &request);
909
910 // Create notification from request
911 PLT_EventNotification* notification = PLT_EventNotification::Parse(request, context, response);
912 NPT_CHECK_POINTER_LABEL_WARNING(notification, bad_request);
913
914 // Give a last change to process pending notifications before throwing them out
915 // by AddPendingNotification
916 ProcessPendingEventNotifications();
917
918 // look for the subscriber with that sid
919 if (NPT_FAILED(NPT_ContainerFind(m_Subscribers,
920 PLT_EventSubscriberFinderBySID(notification->m_SID),
921 sub))) {
922 NPT_LOG_WARNING_1("Subscriber %s not found, delaying notification process.\n", (const char*)notification->m_SID);
923 AddPendingEventNotification(notification);
924 return NPT_SUCCESS;
925 }
926
927 // Process notification for subscriber
928 service = sub->GetService();
929 result = ProcessEventNotification(sub, notification, vars);
930 delete notification;
931
932 NPT_CHECK_LABEL_WARNING(result, bad_request);
933
934 // Notify listeners
935 if (vars.GetItemCount()) {
936 m_ListenerList.Apply(PLT_CtrlPointListenerOnEventNotifyIterator(service, &vars));
937 }
938
939 return NPT_SUCCESS;
940
941 bad_request:
942 NPT_LOG_SEVERE("CtrlPoint received bad event notify request\r\n");
943 if (response.GetStatusCode() == 200) {
944 response.SetStatus(412, "Precondition Failed");
945 }
946 return NPT_SUCCESS;
947 }
948
949 /*----------------------------------------------------------------------
950 | PLT_CtrlPoint::ProcessSsdpSearchResponse
951 +---------------------------------------------------------------------*/
952 NPT_Result
ProcessSsdpSearchResponse(NPT_Result res,const NPT_HttpRequestContext & context,NPT_HttpResponse * response)953 PLT_CtrlPoint::ProcessSsdpSearchResponse(NPT_Result res,
954 const NPT_HttpRequestContext& context,
955 NPT_HttpResponse* response)
956 {
957 NPT_CHECK_SEVERE(res);
958 NPT_CHECK_POINTER_SEVERE(response);
959
960 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
961 NPT_String protocol = response->GetProtocol();
962
963 NPT_String prefix = NPT_String::Format("PLT_CtrlPoint::ProcessSsdpSearchResponse from %s:%d",
964 (const char*)context.GetRemoteAddress().GetIpAddress().ToString() ,
965 context.GetRemoteAddress().GetPort());
966 PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINER, prefix, response);
967
968 // any 2xx responses are ok
969 if (response->GetStatusCode()/100 == 2) {
970 const NPT_String* st = response->GetHeaders().GetHeaderValue("st");
971 const NPT_String* usn = response->GetHeaders().GetHeaderValue("usn");
972 const NPT_String* ext = response->GetHeaders().GetHeaderValue("ext");
973 NPT_CHECK_POINTER_SEVERE(st);
974 NPT_CHECK_POINTER_SEVERE(usn);
975 NPT_CHECK_POINTER_SEVERE(ext);
976
977 NPT_String uuid;
978
979 // if we get an advertisement other than uuid
980 // verify it's formatted properly
981 if (usn != st) {
982 NPT_List<NPT_String> components = usn->Split("::");
983 if (components.GetItemCount() != 2)
984 return NPT_FAILURE;
985
986 if (st->Compare(*components.GetItem(1), true))
987 return NPT_FAILURE;
988
989 uuid = components.GetItem(0)->SubString(5);
990 } else {
991 uuid = usn->SubString(5);
992 }
993
994 if (m_UUIDsToIgnore.Find(NPT_StringFinder(uuid))) {
995 NPT_LOG_FINE_1("CtrlPoint received a search response from ourselves (%s)\n", (const char*)uuid);
996 return NPT_SUCCESS;
997 }
998
999 return ProcessSsdpMessage(*response, context, uuid);
1000 }
1001
1002 return NPT_FAILURE;
1003 }
1004
1005 /*----------------------------------------------------------------------
1006 | PLT_CtrlPoint::OnSsdpPacket
1007 +---------------------------------------------------------------------*/
1008 NPT_Result
OnSsdpPacket(const NPT_HttpRequest & request,const NPT_HttpRequestContext & context)1009 PLT_CtrlPoint::OnSsdpPacket(const NPT_HttpRequest& request,
1010 const NPT_HttpRequestContext& context)
1011 {
1012 return ProcessSsdpNotify(request, context);
1013 }
1014
1015 /*----------------------------------------------------------------------
1016 | PLT_CtrlPoint::ProcessSsdpNotify
1017 +---------------------------------------------------------------------*/
1018 NPT_Result
ProcessSsdpNotify(const NPT_HttpRequest & request,const NPT_HttpRequestContext & context)1019 PLT_CtrlPoint::ProcessSsdpNotify(const NPT_HttpRequest& request,
1020 const NPT_HttpRequestContext& context)
1021 {
1022 // get the address of who sent us some data back
1023 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
1024 NPT_String method = request.GetMethod();
1025 NPT_String uri = request.GetUrl().GetPath(true);
1026 NPT_String protocol = request.GetProtocol();
1027
1028 if (method.Compare("NOTIFY") == 0) {
1029 const NPT_String* nts = PLT_UPnPMessageHelper::GetNTS(request);
1030 const NPT_String* nt = PLT_UPnPMessageHelper::GetNT(request);
1031 const NPT_String* usn = PLT_UPnPMessageHelper::GetUSN(request);
1032
1033 NPT_String prefix = NPT_String::Format("PLT_CtrlPoint::ProcessSsdpNotify from %s:%d (%s)",
1034 context.GetRemoteAddress().GetIpAddress().ToString().GetChars(),
1035 context.GetRemoteAddress().GetPort(),
1036 usn?usn->GetChars():"unknown");
1037 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINER, prefix, &request);
1038
1039 if ((uri.Compare("*") != 0) || (protocol.Compare("HTTP/1.1") != 0))
1040 return NPT_FAILURE;
1041
1042 NPT_CHECK_POINTER_SEVERE(nts);
1043 NPT_CHECK_POINTER_SEVERE(nt);
1044 NPT_CHECK_POINTER_SEVERE(usn);
1045
1046 NPT_String uuid;
1047
1048 // if we get an advertisement other than uuid
1049 // verify it's formatted properly
1050 if (*usn != *nt) {
1051 NPT_List<NPT_String> components = usn->Split("::");
1052 if (components.GetItemCount() != 2)
1053 return NPT_FAILURE;
1054
1055 if (nt->Compare(*components.GetItem(1), true))
1056 return NPT_FAILURE;
1057
1058 uuid = components.GetItem(0)->SubString(5);
1059 } else {
1060 uuid = usn->SubString(5);
1061 }
1062
1063 if (m_UUIDsToIgnore.Find(NPT_StringFinder(uuid))) {
1064 NPT_LOG_FINE_1("Received a NOTIFY request from ourselves (%s)\n", (const char*)uuid);
1065 return NPT_SUCCESS;
1066 }
1067
1068 // if it's a byebye, remove the device and return right away
1069 if (nts->Compare("ssdp:byebye", true) == 0) {
1070 NPT_LOG_INFO_1("Received a byebye NOTIFY request from %s\n", (const char*)uuid);
1071
1072 NPT_AutoLock lock(m_Lock);
1073
1074 // look for root device
1075 PLT_DeviceDataReference root_device;
1076 FindDevice(uuid, root_device, true);
1077
1078 if (!root_device.IsNull()) RemoveDevice(root_device);
1079 return NPT_SUCCESS;
1080 }
1081
1082 return ProcessSsdpMessage(request, context, uuid);
1083 }
1084
1085 return NPT_FAILURE;
1086 }
1087
1088 /*----------------------------------------------------------------------
1089 | PLT_CtrlPoint::AddDevice
1090 +---------------------------------------------------------------------*/
1091 NPT_Result
AddDevice(PLT_DeviceDataReference & data)1092 PLT_CtrlPoint::AddDevice(PLT_DeviceDataReference& data)
1093 {
1094 NPT_AutoLock lock(m_Lock);
1095
1096 return NotifyDeviceReady(data);
1097 }
1098
1099 /*----------------------------------------------------------------------
1100 | PLT_CtrlPoint::NotifyDeviceReady
1101 +---------------------------------------------------------------------*/
1102 NPT_Result
NotifyDeviceReady(PLT_DeviceDataReference & data)1103 PLT_CtrlPoint::NotifyDeviceReady(PLT_DeviceDataReference& data)
1104 {
1105 m_ListenerList.Apply(PLT_CtrlPointListenerOnDeviceAddedIterator(data));
1106
1107 /* recursively add embedded devices */
1108 NPT_Array<PLT_DeviceDataReference> embedded_devices =
1109 data->GetEmbeddedDevices();
1110 for (NPT_Cardinal i=0;i<embedded_devices.GetItemCount();i++) {
1111 NotifyDeviceReady(embedded_devices[i]);
1112 }
1113
1114 return NPT_SUCCESS;
1115 }
1116
1117 /*----------------------------------------------------------------------
1118 | PLT_CtrlPoint::RemoveDevice
1119 +---------------------------------------------------------------------*/
1120 NPT_Result
RemoveDevice(PLT_DeviceDataReference & data)1121 PLT_CtrlPoint::RemoveDevice(PLT_DeviceDataReference& data)
1122 {
1123 NPT_AutoLock lock(m_Lock);
1124
1125 NotifyDeviceRemoved(data);
1126 CleanupDevice(data);
1127
1128 return NPT_SUCCESS;
1129 }
1130
1131 /*----------------------------------------------------------------------
1132 | PLT_CtrlPoint::NotifyDeviceRemoved
1133 +---------------------------------------------------------------------*/
1134 NPT_Result
NotifyDeviceRemoved(PLT_DeviceDataReference & data)1135 PLT_CtrlPoint::NotifyDeviceRemoved(PLT_DeviceDataReference& data)
1136 {
1137 m_ListenerList.Apply(PLT_CtrlPointListenerOnDeviceRemovedIterator(data));
1138
1139 /* recursively add embedded devices */
1140 NPT_Array<PLT_DeviceDataReference> embedded_devices =
1141 data->GetEmbeddedDevices();
1142 for (NPT_Cardinal i=0;i<embedded_devices.GetItemCount();i++) {
1143 NotifyDeviceRemoved(embedded_devices[i]);
1144 }
1145
1146 return NPT_SUCCESS;
1147 }
1148
1149 /*----------------------------------------------------------------------
1150 | PLT_CtrlPoint::CleanupDevice
1151 +---------------------------------------------------------------------*/
1152 NPT_Result
CleanupDevice(PLT_DeviceDataReference & data)1153 PLT_CtrlPoint::CleanupDevice(PLT_DeviceDataReference& data)
1154 {
1155 if (data.IsNull()) return NPT_ERROR_INVALID_PARAMETERS;
1156
1157 NPT_LOG_INFO_1("Removing %s from device list\n", (const char*)data->GetUUID());
1158
1159 // Note: This must take the lock prior to being called
1160 // we can't take the lock here because this function
1161 // will be recursively called if device contains embedded devices
1162
1163 /* recursively remove embedded devices */
1164 NPT_Array<PLT_DeviceDataReference> embedded_devices = data->GetEmbeddedDevices();
1165 for (NPT_Cardinal i=0;i<embedded_devices.GetItemCount();i++) {
1166 CleanupDevice(embedded_devices[i]);
1167 }
1168
1169 /* remove from list */
1170 m_RootDevices.Remove(data);
1171
1172 /* unsubscribe from services */
1173 data->m_Services.Apply(PLT_EventSubscriberRemoverIterator(this));
1174
1175 return NPT_SUCCESS;
1176 }
1177
1178 /*----------------------------------------------------------------------
1179 | PLT_CtrlPoint::ProcessSsdpMessage
1180 +---------------------------------------------------------------------*/
1181 NPT_Result
ProcessSsdpMessage(const NPT_HttpMessage & message,const NPT_HttpRequestContext & context,NPT_String & uuid)1182 PLT_CtrlPoint::ProcessSsdpMessage(const NPT_HttpMessage& message,
1183 const NPT_HttpRequestContext& context,
1184 NPT_String& uuid)
1185 {
1186 NPT_COMPILER_UNUSED(context);
1187
1188 NPT_AutoLock lock(m_Lock);
1189
1190 // check if we should ignore our own UUID
1191 if (m_UUIDsToIgnore.Find(NPT_StringFinder(uuid))) return NPT_SUCCESS;
1192
1193 const NPT_String* url = PLT_UPnPMessageHelper::GetLocation(message);
1194 NPT_CHECK_POINTER_SEVERE(url);
1195
1196 // Fix for Connect360 which uses localhost in device description url
1197 NPT_HttpUrl location(*url);
1198 if (location.GetHost().ToLowercase() == "localhost" ||
1199 location.GetHost().ToLowercase() == "127.0.0.1") {
1200 location.SetHost(context.GetRemoteAddress().GetIpAddress().ToString());
1201 }
1202
1203 // be nice and assume a default lease time if not found even though it's required
1204 NPT_TimeInterval leasetime;
1205 if (NPT_FAILED(PLT_UPnPMessageHelper::GetLeaseTime(message, leasetime))) {
1206 leasetime = *PLT_Constants::GetInstance().GetDefaultSubscribeLease();
1207 }
1208
1209 // check if device (or embedded device) is already known
1210 PLT_DeviceDataReference data;
1211 if (NPT_SUCCEEDED(FindDevice(uuid, data))) {
1212
1213 // // in case we missed the byebye and the device description has changed (ip or port)
1214 // // reset base and assumes device is the same (same number of services and embedded devices)
1215 // // FIXME: The right way is to remove the device and rescan it though but how do we know it changed?
1216 // PLT_DeviceReadyIterator device_tester;
1217 // if (NPT_SUCCEEDED(device_tester(data)) && data->GetDescriptionUrl().Compare(location.ToString(), true)) {
1218 // NPT_LOG_INFO_2("Old device \"%s\" detected @ new location %s",
1219 // (const char*)data->GetFriendlyName(),
1220 // (const char*)location.ToString());
1221 // data->SetURLBase(location);
1222 // }
1223
1224 // renew expiration time
1225 data->SetLeaseTime(leasetime);
1226 NPT_LOG_FINE_1("Device \"%s\" expiration time renewed..",
1227 (const char*)data->GetFriendlyName());
1228
1229 return NPT_SUCCESS;
1230 }
1231
1232 // start inspection
1233 return InspectDevice(location, uuid, leasetime);
1234 }
1235
1236 /*----------------------------------------------------------------------
1237 | PLT_CtrlPoint::InspectDevice
1238 +---------------------------------------------------------------------*/
1239 NPT_Result
InspectDevice(const NPT_HttpUrl & location,const char * uuid,NPT_TimeInterval leasetime)1240 PLT_CtrlPoint::InspectDevice(const NPT_HttpUrl& location,
1241 const char* uuid,
1242 NPT_TimeInterval leasetime)
1243 {
1244 NPT_AutoLock lock(m_Lock);
1245
1246 // check if already inspecting device
1247 NPT_String pending_uuid;
1248 if (NPT_SUCCEEDED(NPT_ContainerFind(m_PendingInspections,
1249 NPT_StringFinder(uuid),
1250 pending_uuid))) {
1251 return NPT_SUCCESS;
1252 }
1253
1254 NPT_LOG_INFO_2("Inspecting device \"%s\" detected @ %s",
1255 uuid,
1256 (const char*)location.ToString());
1257
1258 if (!location.IsValid()) {
1259 NPT_LOG_INFO_1("Invalid device description url: %s",
1260 (const char*) location.ToString());
1261 return NPT_FAILURE;
1262 }
1263
1264 // remember that we're now inspecting the device
1265 m_PendingInspections.Add(uuid);
1266
1267 // Start a task to retrieve the description
1268 PLT_CtrlPointGetDescriptionTask* task = new PLT_CtrlPointGetDescriptionTask(
1269 location,
1270 this,
1271 leasetime,
1272 uuid);
1273
1274 // Add a delay to make sure that we received late NOTIFY bye-bye
1275 NPT_TimeInterval delay(.5f);
1276 m_TaskManager->StartTask(task, &delay);
1277
1278 return NPT_SUCCESS;
1279 }
1280
1281 /*----------------------------------------------------------------------
1282 | PLT_CtrlPoint::FetchDeviceSCPDs
1283 +---------------------------------------------------------------------*/
1284 NPT_Result
FetchDeviceSCPDs(PLT_CtrlPointGetSCPDsTask * task,PLT_DeviceDataReference & device,NPT_Cardinal level)1285 PLT_CtrlPoint::FetchDeviceSCPDs(PLT_CtrlPointGetSCPDsTask* task,
1286 PLT_DeviceDataReference& device,
1287 NPT_Cardinal level)
1288 {
1289 if (level == 5 && device->m_EmbeddedDevices.GetItemCount()) {
1290 NPT_LOG_FATAL("Too many embedded devices depth! ");
1291 return NPT_FAILURE;
1292 }
1293
1294 ++level;
1295
1296 // fetch embedded devices services scpds first
1297 for (NPT_Cardinal i = 0;
1298 i<device->m_EmbeddedDevices.GetItemCount();
1299 i++) {
1300 NPT_CHECK_SEVERE(FetchDeviceSCPDs(task, device->m_EmbeddedDevices[i], level));
1301 }
1302
1303 // Get SCPD of device services now and bail right away if one fails
1304 return device->m_Services.ApplyUntil(
1305 PLT_AddGetSCPDRequestIterator(*task, device),
1306 NPT_UntilResultNotEquals(NPT_SUCCESS));
1307 }
1308
1309 /*----------------------------------------------------------------------
1310 | PLT_CtrlPoint::ProcessGetDescriptionResponse
1311 +---------------------------------------------------------------------*/
1312 NPT_Result
ProcessGetDescriptionResponse(NPT_Result res,const NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse * response,NPT_TimeInterval leasetime,NPT_String uuid)1313 PLT_CtrlPoint::ProcessGetDescriptionResponse(NPT_Result res,
1314 const NPT_HttpRequest& request,
1315 const NPT_HttpRequestContext& context,
1316 NPT_HttpResponse* response,
1317 NPT_TimeInterval leasetime,
1318 NPT_String uuid)
1319 {
1320 NPT_COMPILER_UNUSED(request);
1321
1322 NPT_AutoLock lock(m_Lock);
1323
1324 PLT_CtrlPointGetSCPDsTask* task = NULL;
1325 NPT_String desc;
1326 PLT_DeviceDataReference root_device;
1327 PLT_DeviceDataReference device;
1328
1329 // Add a delay, some devices need it (aka Rhapsody)
1330 NPT_TimeInterval delay(0.1f);
1331
1332 NPT_String prefix = NPT_String::Format("PLT_CtrlPoint::ProcessGetDescriptionResponse @ %s (result = %d, status = %d)",
1333 (const char*)request.GetUrl().ToString(),
1334 res,
1335 response?response->GetStatusCode():0);
1336
1337 // Remove pending inspection
1338 m_PendingInspections.Remove(uuid);
1339
1340 // verify response was ok
1341 NPT_CHECK_LABEL_FATAL(res, bad_response);
1342 NPT_CHECK_POINTER_LABEL_FATAL(response, bad_response);
1343
1344 // log response
1345 PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINER, prefix, response);
1346
1347 // get response body
1348 res = PLT_HttpHelper::GetBody(*response, desc);
1349 NPT_CHECK_SEVERE(res);
1350
1351 // create new root device
1352 NPT_CHECK_SEVERE(PLT_DeviceData::SetDescription(root_device, leasetime, request.GetUrl(), desc, context));
1353
1354 // make sure root device was not previously queried
1355 if (NPT_FAILED(FindDevice(root_device->GetUUID(), device))) {
1356 m_RootDevices.Add(root_device);
1357
1358 NPT_LOG_INFO_3("Device \"%s\" is now known as \"%s\" (%s)",
1359 (const char*)root_device->GetUUID(),
1360 (const char*)root_device->GetFriendlyName(),
1361 (const char*)root_device->GetDescriptionUrl(NULL));
1362
1363 // create one single task to fetch all scpds one after the other
1364 task = new PLT_CtrlPointGetSCPDsTask(this, root_device);
1365 NPT_CHECK_LABEL_SEVERE(res = FetchDeviceSCPDs(task, root_device, 0),
1366 cleanup);
1367
1368 // if device has embedded devices, we want to delay fetching scpds
1369 // just in case there's a chance all the initial NOTIFY bye-bye have
1370 // not all been received yet which would cause to remove the devices
1371 // as we're adding them
1372 if (root_device->m_EmbeddedDevices.GetItemCount() > 0) {
1373 delay = 1.f;
1374 }
1375 NPT_CHECK_LABEL_SEVERE(res = m_TaskManager->StartTask(task, &delay),
1376 failure);
1377 }
1378
1379 return NPT_SUCCESS;
1380
1381 bad_response:
1382 NPT_LOG_SEVERE_2("Bad Description response @ %s: %s",
1383 (const char*)request.GetUrl().ToString(),
1384 (const char*)desc);
1385
1386 cleanup:
1387 if (task) delete task;
1388
1389 failure:
1390 return res;
1391 }
1392
1393 /*----------------------------------------------------------------------
1394 | PLT_CtrlPoint::ProcessGetSCPDResponse
1395 +---------------------------------------------------------------------*/
1396 NPT_Result
ProcessGetSCPDResponse(NPT_Result res,const NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse * response,PLT_DeviceDataReference & device)1397 PLT_CtrlPoint::ProcessGetSCPDResponse(NPT_Result res,
1398 const NPT_HttpRequest& request,
1399 const NPT_HttpRequestContext& context,
1400 NPT_HttpResponse* response,
1401 PLT_DeviceDataReference& device)
1402 {
1403 NPT_COMPILER_UNUSED(context);
1404
1405 NPT_AutoLock lock(m_Lock);
1406
1407 PLT_DeviceReadyIterator device_tester;
1408 NPT_String scpd;
1409 PLT_DeviceDataReference root_device;
1410 PLT_Service* service;
1411
1412 NPT_String prefix = NPT_String::Format("PLT_CtrlPoint::ProcessGetSCPDResponse for a service of device \"%s\" @ %s (result = %d, status = %d)",
1413 (const char*)device->GetFriendlyName(),
1414 (const char*)request.GetUrl().ToString(),
1415 res,
1416 response?response->GetStatusCode():0);
1417
1418 // verify response was ok
1419 NPT_CHECK_LABEL_FATAL(res, bad_response);
1420 NPT_CHECK_POINTER_LABEL_FATAL(response, bad_response);
1421
1422 PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINER, prefix, response);
1423
1424 // make sure root device hasn't disappeared
1425 NPT_CHECK_LABEL_WARNING(FindDevice(device->GetUUID(), root_device, true),
1426 bad_response);
1427
1428 res = device->FindServiceBySCPDURL(request.GetUrl().ToRequestString(), service);
1429 NPT_CHECK_LABEL_SEVERE(res, bad_response);
1430
1431 // get response body
1432 res = PLT_HttpHelper::GetBody(*response, scpd);
1433 NPT_CHECK_LABEL_FATAL(res, bad_response);
1434
1435 // DIAL support
1436 if (root_device->GetType().Compare("urn:dial-multiscreen-org:device:dial:1") == 0) {
1437 AddDevice(root_device);
1438 return NPT_SUCCESS;
1439 }
1440
1441 // set the service scpd
1442 res = service->SetSCPDXML(scpd);
1443 NPT_CHECK_LABEL_SEVERE(res, bad_response);
1444
1445 // if root device is ready, notify listeners about it and embedded devices
1446 if (NPT_SUCCEEDED(device_tester(root_device))) {
1447 AddDevice(root_device);
1448 }
1449
1450 return NPT_SUCCESS;
1451
1452 bad_response:
1453 NPT_LOG_SEVERE_2("Bad SCPD response for device \"%s\":%s",
1454 (const char*)device->GetFriendlyName(),
1455 (const char*)scpd);
1456
1457 if (!root_device.IsNull()) RemoveDevice(root_device);
1458 return res;
1459 }
1460
1461 /*----------------------------------------------------------------------
1462 | PLT_CtrlPoint::RenewSubscriber
1463 +---------------------------------------------------------------------*/
1464 PLT_ThreadTask*
RenewSubscriber(PLT_EventSubscriberReference subscriber)1465 PLT_CtrlPoint::RenewSubscriber(PLT_EventSubscriberReference subscriber)
1466 {
1467 NPT_AutoLock lock(m_Lock);
1468
1469 PLT_DeviceDataReference root_device;
1470 if (NPT_FAILED(FindDevice(subscriber->GetService()->GetDevice()->GetUUID(),
1471 root_device,
1472 true))) {
1473 return NULL;
1474 }
1475
1476 NPT_LOG_FINE_3("Renewing subscriber \"%s\" for service \"%s\" of device \"%s\"",
1477 (const char*)subscriber->GetSID(),
1478 (const char*)subscriber->GetService()->GetServiceID(),
1479 (const char*)subscriber->GetService()->GetDevice()->GetFriendlyName());
1480
1481 // create the request
1482 NPT_HttpRequest* request = new NPT_HttpRequest(
1483 subscriber->GetService()->GetEventSubURL(true),
1484 "SUBSCRIBE",
1485 NPT_HTTP_PROTOCOL_1_1);
1486
1487 PLT_UPnPMessageHelper::SetSID(*request, subscriber->GetSID());
1488 PLT_UPnPMessageHelper::SetTimeOut(*request,
1489 (NPT_Int32)PLT_Constants::GetInstance().GetDefaultSubscribeLease()->ToSeconds());
1490
1491 // Prepare the request
1492 // create a task to post the request
1493 return new PLT_CtrlPointSubscribeEventTask(
1494 request,
1495 this,
1496 root_device,
1497 subscriber->GetService());
1498 }
1499
1500 /*----------------------------------------------------------------------
1501 | PLT_CtrlPoint::Subscribe
1502 +---------------------------------------------------------------------*/
1503 NPT_Result
Subscribe(PLT_Service * service,bool cancel,void * userdata)1504 PLT_CtrlPoint::Subscribe(PLT_Service* service,
1505 bool cancel,
1506 void* userdata)
1507 {
1508 NPT_AutoLock lock(m_Lock);
1509
1510 if (!m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
1511
1512 NPT_HttpRequest* request = NULL;
1513
1514 // make sure service is subscribable
1515 if (!service->IsSubscribable()) return NPT_FAILURE;
1516
1517 // event url
1518 NPT_HttpUrl url(service->GetEventSubURL(true));
1519
1520 // look for the corresponding root device & sub
1521 PLT_DeviceDataReference root_device;
1522 PLT_EventSubscriberReference sub;
1523 NPT_CHECK_WARNING(FindDevice(service->GetDevice()->GetUUID(),
1524 root_device,
1525 true));
1526
1527 // look for the subscriber with that service to decide if it's a renewal or not
1528 NPT_ContainerFind(m_Subscribers,
1529 PLT_EventSubscriberFinderByService(service),
1530 sub);
1531
1532 if (cancel == false) {
1533 // renewal?
1534 if (!sub.IsNull()) {
1535 PLT_ThreadTask* task = RenewSubscriber(sub);
1536 return m_TaskManager->StartTask(task);
1537 }
1538
1539 NPT_LOG_INFO_2("Subscribing to service \"%s\" of device \"%s\"",
1540 (const char*)service->GetServiceID(),
1541 (const char*)service->GetDevice()->GetFriendlyName());
1542
1543 // prepare the callback url
1544 NPT_String uuid = service->GetDevice()->GetUUID();
1545 NPT_String service_id = service->GetServiceID();
1546 NPT_String callback_uri = "/" + uuid + "/" + service_id;
1547
1548 // create the request
1549 request = new NPT_HttpRequest(url, "SUBSCRIBE", NPT_HTTP_PROTOCOL_1_1);
1550 // specify callback url using ip of interface used when
1551 // retrieving device description
1552 NPT_HttpUrl callbackUrl(
1553 service->GetDevice()->m_LocalIfaceIp.ToString(),
1554 m_EventHttpServer->GetPort(),
1555 callback_uri);
1556
1557 // set the required headers for a new subscription
1558 PLT_UPnPMessageHelper::SetNT(*request, "upnp:event");
1559 PLT_UPnPMessageHelper::SetCallbacks(*request,
1560 "<" + callbackUrl.ToString() + ">");
1561 PLT_UPnPMessageHelper::SetTimeOut(*request,
1562 (NPT_Int32)PLT_Constants::GetInstance().GetDefaultSubscribeLease()->ToSeconds());
1563 } else {
1564 NPT_LOG_INFO_3("Unsubscribing subscriber \"%s\" for service \"%s\" of device \"%s\"",
1565 (const char*)(!sub.IsNull()?sub->GetSID().GetChars():"unknown"),
1566 (const char*)service->GetServiceID(),
1567 (const char*)service->GetDevice()->GetFriendlyName());
1568
1569 // cancellation
1570 if (sub.IsNull()) return NPT_FAILURE;
1571
1572 // create the request
1573 request = new NPT_HttpRequest(url, "UNSUBSCRIBE", NPT_HTTP_PROTOCOL_1_1);
1574 PLT_UPnPMessageHelper::SetSID(*request, sub->GetSID());
1575
1576 // remove from list now
1577 m_Subscribers.Remove(sub, true);
1578 }
1579
1580 // verify we have request to send just in case
1581 NPT_CHECK_POINTER_FATAL(request);
1582
1583 // Prepare the request
1584 // create a task to post the request
1585 PLT_ThreadTask* task = new PLT_CtrlPointSubscribeEventTask(
1586 request,
1587 this,
1588 root_device,
1589 service,
1590 userdata);
1591 m_TaskManager->StartTask(task);
1592
1593 return NPT_SUCCESS;
1594 }
1595
1596 /*----------------------------------------------------------------------
1597 | PLT_CtrlPoint::ProcessSubscribeResponse
1598 +---------------------------------------------------------------------*/
1599 NPT_Result
ProcessSubscribeResponse(NPT_Result res,const NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse * response,PLT_Service * service,void *)1600 PLT_CtrlPoint::ProcessSubscribeResponse(NPT_Result res,
1601 const NPT_HttpRequest& request,
1602 const NPT_HttpRequestContext& context,
1603 NPT_HttpResponse* response,
1604 PLT_Service* service,
1605 void* /* userdata */)
1606 {
1607 NPT_COMPILER_UNUSED(context);
1608
1609 NPT_AutoLock lock(m_Lock);
1610
1611 const NPT_String* sid = NULL;
1612 NPT_Int32 seconds = -1;
1613 PLT_EventSubscriberReference sub;
1614 bool subscription = (request.GetMethod().ToUppercase() == "SUBSCRIBE");
1615
1616 NPT_String prefix = NPT_String::Format("PLT_CtrlPoint::ProcessSubscribeResponse %ubscribe for service \"%s\" (result = %d, status code = %d)",
1617 (const char*)subscription?"S":"Uns",
1618 (const char*)service->GetServiceID(),
1619 res,
1620 response?response->GetStatusCode():0);
1621 PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINER, prefix, response);
1622
1623 // if there's a failure or it's a response to a cancellation
1624 // we get out (any 2xx status code ok)
1625 if (NPT_FAILED(res) || response == NULL || response->GetStatusCode()/100 != 2) {
1626 goto failure;
1627 }
1628
1629 if (subscription) {
1630 if (!(sid = PLT_UPnPMessageHelper::GetSID(*response)) ||
1631 NPT_FAILED(PLT_UPnPMessageHelper::GetTimeOut(*response, seconds))) {
1632 NPT_CHECK_LABEL_SEVERE(res = NPT_ERROR_INVALID_SYNTAX, failure);
1633 }
1634
1635 // Look for subscriber
1636 NPT_ContainerFind(m_Subscribers,
1637 PLT_EventSubscriberFinderBySID(*sid),
1638 sub);
1639
1640 NPT_LOG_INFO_5("%s subscriber \"%s\" for service \"%s\" of device \"%s\" (timeout = %d)",
1641 !sub.IsNull()?"Updating timeout for":"Creating new",
1642 (const char*)*sid,
1643 (const char*)service->GetServiceID(),
1644 (const char*)service->GetDevice()->GetFriendlyName(),
1645 seconds);
1646
1647 // create new subscriber if sid never seen before
1648 // or update subscriber expiration otherwise
1649 if (sub.IsNull()) {
1650 sub = new PLT_EventSubscriber(m_TaskManager, service, *sid, seconds);
1651 m_Subscribers.Add(sub);
1652 } else {
1653 sub->SetTimeout(seconds);
1654 }
1655
1656 // Process any pending notifcations for that subscriber we got a bit too early
1657 ProcessPendingEventNotifications();
1658
1659 return NPT_SUCCESS;
1660 }
1661
1662 goto remove_sub;
1663
1664 failure:
1665 NPT_LOG_SEVERE_4("%subscription failed of sub \"%s\" for service \"%s\" of device \"%s\"",
1666 (const char*)subscription?"S":"Uns",
1667 (const char*)(sid?*sid:"Unknown"),
1668 (const char*)service->GetServiceID(),
1669 (const char*)service->GetDevice()->GetFriendlyName());
1670 res = NPT_FAILED(res)?res:NPT_FAILURE;
1671
1672 remove_sub:
1673 // in case it was a renewal look for the subscriber with that service and remove it from the list
1674 if (NPT_SUCCEEDED(NPT_ContainerFind(m_Subscribers,
1675 PLT_EventSubscriberFinderByService(service),
1676 sub))) {
1677 m_Subscribers.Remove(sub);
1678 }
1679
1680 return res;
1681 }
1682
1683 /*----------------------------------------------------------------------
1684 | PLT_CtrlPoint::InvokeAction
1685 +---------------------------------------------------------------------*/
1686 NPT_Result
InvokeAction(PLT_ActionReference & action,void * userdata)1687 PLT_CtrlPoint::InvokeAction(PLT_ActionReference& action,
1688 void* userdata)
1689 {
1690 if (!m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
1691
1692 PLT_Service* service = action->GetActionDesc().GetService();
1693
1694 // create the request
1695 NPT_HttpUrl url(service->GetControlURL(true));
1696 NPT_HttpRequest* request = new NPT_HttpRequest(url, "POST", NPT_HTTP_PROTOCOL_1_1);
1697
1698 // create a memory stream for our request body
1699 NPT_MemoryStreamReference stream(new NPT_MemoryStream);
1700 action->FormatSoapRequest(*stream);
1701
1702 // set the request body
1703 NPT_HttpEntity* entity = NULL;
1704 PLT_HttpHelper::SetBody(*request, (NPT_InputStreamReference)stream, &entity);
1705
1706 entity->SetContentType("text/xml; charset=\"utf-8\"");
1707 NPT_String service_type = service->GetServiceType();
1708 NPT_String action_name = action->GetActionDesc().GetName();
1709 request->GetHeaders().SetHeader("SOAPAction", "\"" + service_type + "#" + action_name + "\"");
1710
1711 // create a task to post the request
1712 PLT_CtrlPointInvokeActionTask* task = new PLT_CtrlPointInvokeActionTask(
1713 request,
1714 this,
1715 action,
1716 userdata);
1717
1718 // queue the request
1719 m_TaskManager->StartTask(task);
1720
1721 return NPT_SUCCESS;
1722 }
1723
1724 /*----------------------------------------------------------------------
1725 | PLT_CtrlPoint::ProcessActionResponse
1726 +---------------------------------------------------------------------*/
1727 NPT_Result
ProcessActionResponse(NPT_Result res,const NPT_HttpRequest & request,const NPT_HttpRequestContext &,NPT_HttpResponse * response,PLT_ActionReference & action,void * userdata)1728 PLT_CtrlPoint::ProcessActionResponse(NPT_Result res,
1729 const NPT_HttpRequest& request,
1730 const NPT_HttpRequestContext& /*context*/,
1731 NPT_HttpResponse* response,
1732 PLT_ActionReference& action,
1733 void* userdata)
1734 {
1735 NPT_COMPILER_UNUSED(request);
1736
1737 NPT_String service_type;
1738 NPT_String str;
1739 NPT_XmlElementNode* xml = NULL;
1740 NPT_String name;
1741 NPT_String soap_action_name;
1742 NPT_XmlElementNode* soap_action_response;
1743 NPT_XmlElementNode* soap_body;
1744 NPT_XmlElementNode* fault;
1745 const NPT_String* attr = NULL;
1746 PLT_ActionDesc& action_desc = action->GetActionDesc();
1747
1748 // reset the error code and desc
1749 action->SetError(0, "");
1750
1751 // check context validity
1752 if (NPT_FAILED(res) || response == NULL) {
1753 PLT_Service* service = action_desc.GetService();
1754 NPT_COMPILER_UNUSED(service);
1755 NPT_LOG_WARNING_4("Failed to reach %s for %s.%s (%d)",
1756 request.GetUrl().ToString().GetChars(),
1757 service->GetDevice()->GetUUID().GetChars(),
1758 service->GetServiceName().GetChars(),
1759 res);
1760 goto failure;
1761 }
1762
1763 PLT_LOG_HTTP_RESPONSE(NPT_LOG_LEVEL_FINER, "PLT_CtrlPoint::ProcessActionResponse:", response);
1764
1765 NPT_LOG_FINER("Reading/Parsing Action Response Body...");
1766 if (NPT_FAILED(PLT_HttpHelper::ParseBody(*response, xml))) {
1767 goto failure;
1768 }
1769
1770 NPT_LOG_FINER("Analyzing Action Response Body...");
1771
1772 // read envelope
1773 if (xml->GetTag().Compare("Envelope", true))
1774 goto failure;
1775
1776 // check namespace
1777 if (!xml->GetNamespace() || xml->GetNamespace()->Compare("http://schemas.xmlsoap.org/soap/envelope/"))
1778 goto failure;
1779
1780 // check encoding
1781 attr = xml->GetAttribute("encodingStyle", "http://schemas.xmlsoap.org/soap/envelope/");
1782 if (!attr || attr->Compare("http://schemas.xmlsoap.org/soap/encoding/"))
1783 goto failure;
1784
1785 // read action
1786 soap_body = PLT_XmlHelper::GetChild(xml, "Body");
1787 if (soap_body == NULL)
1788 goto failure;
1789
1790 // check if an error occurred
1791 fault = PLT_XmlHelper::GetChild(soap_body, "Fault");
1792 if (fault != NULL) {
1793 // we have an error
1794 ParseFault(action, fault);
1795 goto failure;
1796 }
1797
1798 if (NPT_FAILED(PLT_XmlHelper::GetChild(soap_body, soap_action_response)))
1799 goto failure;
1800
1801 // verify action name is identical to SOAPACTION header
1802 if (soap_action_response->GetTag().Compare(action_desc.GetName() + "Response", true))
1803 goto failure;
1804
1805 // verify namespace
1806 if (!soap_action_response->GetNamespace() ||
1807 soap_action_response->GetNamespace()->Compare(action_desc.GetService()->GetServiceType()))
1808 goto failure;
1809
1810 // read all the arguments if any
1811 for (NPT_List<NPT_XmlNode*>::Iterator args = soap_action_response->GetChildren().GetFirstItem();
1812 args;
1813 args++) {
1814 NPT_XmlElementNode* child = (*args)->AsElementNode();
1815 if (!child) continue;
1816
1817 action->SetArgumentValue(child->GetTag(), child->GetText()?*child->GetText():"");
1818 if (NPT_FAILED(res)) goto failure;
1819 }
1820
1821 // create a buffer for our response body and call the service
1822 res = action->VerifyArguments(false);
1823 if (NPT_FAILED(res)) goto failure;
1824
1825 goto cleanup;
1826
1827 failure:
1828 // override res with failure if necessary
1829 if (NPT_SUCCEEDED(res)) res = NPT_FAILURE;
1830 // fallthrough
1831
1832 cleanup:
1833 {
1834 NPT_AutoLock lock(m_Lock);
1835 m_ListenerList.Apply(PLT_CtrlPointListenerOnActionResponseIterator(res, action, userdata));
1836 }
1837
1838 delete xml;
1839 return res;
1840 }
1841
1842 /*----------------------------------------------------------------------
1843 | PLT_CtrlPoint::ParseFault
1844 +---------------------------------------------------------------------*/
1845 NPT_Result
ParseFault(PLT_ActionReference & action,NPT_XmlElementNode * fault)1846 PLT_CtrlPoint::ParseFault(PLT_ActionReference& action,
1847 NPT_XmlElementNode* fault)
1848 {
1849 NPT_XmlElementNode* detail = fault->GetChild("detail");
1850 if (detail == NULL) return NPT_FAILURE;
1851
1852 NPT_XmlElementNode *upnp_error, *error_code, *error_desc;
1853 upnp_error = detail->GetChild("upnp_error");
1854
1855 // WMP12 Hack
1856 if (upnp_error == NULL) {
1857 upnp_error = detail->GetChild("UPnPError", NPT_XML_ANY_NAMESPACE);
1858 if (upnp_error == NULL) return NPT_FAILURE;
1859 }
1860
1861 error_code = upnp_error->GetChild("errorCode", NPT_XML_ANY_NAMESPACE);
1862 error_desc = upnp_error->GetChild("errorDescription", NPT_XML_ANY_NAMESPACE);
1863 NPT_Int32 code = 501;
1864 NPT_String desc;
1865 if (error_code && error_code->GetText()) {
1866 NPT_String value = *error_code->GetText();
1867 value.ToInteger(code);
1868 }
1869 if (error_desc && error_desc->GetText()) {
1870 desc = *error_desc->GetText();
1871 }
1872 action->SetError(code, desc);
1873 return NPT_SUCCESS;
1874 }
1875