comparison deps/Platinum/Source/Core/PltDeviceHost.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 - Device Host
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 "PltDeviceHost.h"
39 #include "PltUPnP.h"
40 #include "PltXmlHelper.h"
41 #include "PltSsdp.h"
42 #include "PltHttpServer.h"
43 #include "PltVersion.h"
44
45 NPT_SET_LOCAL_LOGGER("platinum.core.devicehost")
46
47 /*----------------------------------------------------------------------
48 | typedef
49 +---------------------------------------------------------------------*/
50 typedef PLT_HttpRequestHandler<PLT_DeviceHost> PLT_HttpDeviceHostRequestHandler;
51
52 /*----------------------------------------------------------------------
53 | PLT_DeviceHost::PLT_DeviceHost
54 +---------------------------------------------------------------------*/
55 PLT_DeviceHost::PLT_DeviceHost(const char* description_path /* = "/" */,
56 const char* uuid /* = "" */,
57 const char* device_type /* = "" */,
58 const char* friendly_name /* = "" */,
59 bool show_ip /* = false */,
60 NPT_UInt16 port /* = 0 */,
61 bool port_rebind /* = false */) :
62 PLT_DeviceData(NPT_HttpUrl(NULL, 0, description_path),
63 uuid,
64 NPT_TimeInterval(1800, 0),
65 device_type,
66 friendly_name),
67 m_HttpServer(NULL),
68 m_Broadcast(false),
69 m_Port(port),
70 m_PortRebind(port_rebind)
71 {
72 if (show_ip) {
73 NPT_List<NPT_String> ips;
74 PLT_UPnPMessageHelper::GetIPAddresses(ips);
75 if (ips.GetItemCount()) {
76 m_FriendlyName += " (" + *ips.GetFirstItem() + ")";
77 }
78 }
79 }
80
81 /*----------------------------------------------------------------------
82 | PLT_DeviceHost::~PLT_DeviceHost
83 +---------------------------------------------------------------------*/
84 PLT_DeviceHost::~PLT_DeviceHost()
85 {
86 }
87
88 /*----------------------------------------------------------------------
89 | PLT_DeviceHost::AddIcon
90 +---------------------------------------------------------------------*/
91 NPT_Result
92 PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon, const char* filepath)
93 {
94 NPT_HttpFileRequestHandler* icon_handler =
95 new NPT_HttpFileRequestHandler(icon.m_UrlPath, filepath);
96 m_HttpServer->AddRequestHandler(icon_handler, icon.m_UrlPath, false);
97 m_RequestHandlers.Add(icon_handler);
98 return m_Icons.Add(icon);
99 }
100
101 /*----------------------------------------------------------------------
102 | PLT_DeviceHost::AddIcon
103 +---------------------------------------------------------------------*/
104 NPT_Result
105 PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon,
106 const void* data,
107 NPT_Size size,
108 bool copy /* = true */)
109 {
110 NPT_HttpStaticRequestHandler* icon_handler =
111 new NPT_HttpStaticRequestHandler(
112 data,
113 size,
114 icon.m_MimeType,
115 copy);
116 m_HttpServer->AddRequestHandler(icon_handler, icon.m_UrlPath, false);
117 m_RequestHandlers.Add(icon_handler);
118 return m_Icons.Add(icon);
119 }
120
121 /*----------------------------------------------------------------------
122 | PLT_DeviceHost::SetupIcons
123 +---------------------------------------------------------------------*/
124 NPT_Result
125 PLT_DeviceHost::SetupIcons()
126 {
127 AddIcon(
128 PLT_DeviceIcon("image/jpeg", 120, 120, 24, "/images/platinum-120x120.jpg"),
129 "platinum-120x120.jpg");
130 AddIcon(
131 PLT_DeviceIcon("image/jpeg", 48, 48, 24, "/images/platinum-48x48.jpg"),
132 "platinum-48x48.jpg");
133 AddIcon(
134 PLT_DeviceIcon("image/png", 120, 120, 24, "/images/platinum-120x120.png"),
135 "platinum-120x120.png");
136 AddIcon(
137 PLT_DeviceIcon("image/png", 48, 48, 24, "/images/platinum-48x48.png"),
138 "platinum-48x48.png");
139 return NPT_SUCCESS;
140 }
141
142 /*----------------------------------------------------------------------
143 | PLT_DeviceHost::SetupDevice
144 +---------------------------------------------------------------------*/
145 NPT_Result
146 PLT_DeviceHost::SetupDevice()
147 {
148 NPT_CHECK_WARNING(SetupIcons());
149 return NPT_SUCCESS;
150 }
151
152 /*----------------------------------------------------------------------
153 | PLT_DeviceHost::SetupServiceSCPDHandler
154 +---------------------------------------------------------------------*/
155 NPT_Result
156 PLT_DeviceHost::SetupServiceSCPDHandler(PLT_Service* service)
157 {
158 NPT_HttpUrl url;
159 NPT_String doc;
160
161 // static scpd document
162 NPT_String scpd_url = service->GetSCPDURL();
163 if (!scpd_url.StartsWith("/")) {
164 scpd_url = GetURLBase().GetPath() + scpd_url;
165 }
166 url.SetPathPlus(scpd_url);
167 NPT_CHECK_FATAL(service->GetSCPDXML(doc));
168
169 NPT_HttpStaticRequestHandler* scpd_handler = new NPT_HttpStaticRequestHandler(doc, "text/xml");
170 m_HttpServer->AddRequestHandler(scpd_handler, url.GetPath(), false);
171 m_RequestHandlers.Add(scpd_handler);
172 return NPT_SUCCESS;
173 }
174
175 /*----------------------------------------------------------------------
176 | PLT_DeviceHost::Start
177 +---------------------------------------------------------------------*/
178 NPT_Result
179 PLT_DeviceHost::Start(PLT_SsdpListenTask* task)
180 {
181 // TODO: We should reuse address otherwise we might fail if we're passed the same port during fast restart
182 #ifdef _XBOX
183 m_HttpServer = new PLT_HttpServer(m_Port, m_PortRebind, 5);
184 #else
185 m_HttpServer = new PLT_HttpServer(m_Port, m_PortRebind, 20); // limit to 20 threads max
186 #endif
187
188 NPT_CHECK_FATAL(SetupServices(*this));
189
190 // start the server
191 NPT_CHECK_SEVERE(m_HttpServer->Start());
192
193 // read back assigned port in case
194 // we passed 0 to randomly select one
195 m_Port = m_HttpServer->GetPort();
196 m_URLDescription.SetPort(m_Port);
197
198 // callback to initialize the device
199 NPT_CHECK_FATAL(SetupDevice());
200
201 // set up static handlers first as the order is important
202
203 // services static root device scpd documents
204 for (NPT_Cardinal i=0; i<m_Services.GetItemCount(); i++) {
205 SetupServiceSCPDHandler(m_Services[i]);
206 }
207
208 // services static embedded devices scpd documents
209 for (NPT_Cardinal j=0; j<m_EmbeddedDevices.GetItemCount(); j++) {
210 for (NPT_Cardinal i=0; i<m_EmbeddedDevices[j]->m_Services.GetItemCount(); i++) {
211 SetupServiceSCPDHandler(m_EmbeddedDevices[j]->m_Services[i]);
212 }
213 }
214
215 // all other requests including description document
216 // and service control are dynamically handled
217 PLT_HttpDeviceHostRequestHandler* device_handler = new PLT_HttpDeviceHostRequestHandler(this);
218 m_RequestHandlers.Add(device_handler);
219 m_HttpServer->AddRequestHandler(device_handler, "/", true);
220
221 // we should not advertise right away
222 // spec says randomly less than 100ms
223 NPT_TimeInterval delay(0, NPT_System::GetRandomInteger() % 100000000);
224
225 // calculate when we should send another announcement
226 NPT_Size leaseTime = (NPT_Size)(float)GetLeaseTime();
227 NPT_TimeInterval repeat;
228 repeat.m_Seconds = leaseTime?(int)((leaseTime >> 1) + ((unsigned short)NPT_System::GetRandomInteger() % (leaseTime >> 2))):30;
229
230 // the XBOX cannot receive multicast, so we blast every 7 secs
231 #ifdef _XBOX
232 repeat.m_Seconds = 7;
233 #endif
234
235 PLT_ThreadTask* announce_task = new PLT_SsdpDeviceAnnounceTask(
236 this,
237 repeat,
238 true,
239 m_Broadcast);
240 m_TaskManager.StartTask(announce_task, &delay);
241
242 // register ourselves as a listener for ssdp requests
243 task->AddListener(this);
244 return NPT_SUCCESS;
245 }
246
247 /*----------------------------------------------------------------------
248 | PLT_DeviceHost::Stop
249 +---------------------------------------------------------------------*/
250 NPT_Result
251 PLT_DeviceHost::Stop(PLT_SsdpListenTask* task)
252 {
253 // unregister ourselves as a listener for ssdp requests
254 task->RemoveListener(this);
255
256 // remove all our running tasks
257 m_TaskManager.StopAllTasks();
258
259 if (m_HttpServer) {
260 // stop our internal http server
261 m_HttpServer->Stop();
262 delete m_HttpServer;
263 m_HttpServer = NULL;
264
265 // notify we're gone
266 NPT_List<NPT_NetworkInterface*> if_list;
267 PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list);
268 if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(this, true, m_Broadcast));
269 if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
270 }
271
272 m_RequestHandlers.Apply(NPT_ObjectDeleter<NPT_HttpRequestHandler>());
273 m_RequestHandlers.Clear();
274
275 PLT_DeviceData::Cleanup();
276 return NPT_SUCCESS;
277 }
278
279 /*----------------------------------------------------------------------
280 | PLT_DeviceHost::Announce
281 +---------------------------------------------------------------------*/
282 NPT_Result
283 PLT_DeviceHost::Announce(PLT_DeviceData* device,
284 NPT_HttpRequest& req,
285 NPT_UdpSocket& socket,
286 bool byebye)
287 {
288 NPT_Result res = NPT_SUCCESS;
289
290 NPT_LOG_INFO_3("Sending SSDP NOTIFY (%s) Request to %s with location:%s",
291 byebye?"ssdp:byebye":"ssdp:alive",
292 (const char*)req.GetUrl().ToString(),
293 (const char*)(PLT_UPnPMessageHelper::GetLocation(req)?*PLT_UPnPMessageHelper::GetLocation(req):""));
294
295 if (byebye == false) {
296 // get location URL based on ip address of interface
297 PLT_UPnPMessageHelper::SetNTS(req, "ssdp:alive");
298 PLT_UPnPMessageHelper::SetLeaseTime(req, (NPT_Timeout)(float)device->GetLeaseTime());
299 PLT_UPnPMessageHelper::SetServer(req, NPT_HttpServer::m_ServerHeader, false);
300 } else {
301 PLT_UPnPMessageHelper::SetNTS(req, "ssdp:byebye");
302 }
303
304 // target address
305 NPT_IpAddress ip;
306 if (NPT_FAILED(res = ip.ResolveName(req.GetUrl().GetHost()))) {
307 return res;
308 }
309 NPT_SocketAddress addr(ip, req.GetUrl().GetPort());
310
311 // upnp:rootdevice
312 if (device->m_ParentUUID.IsEmpty()) {
313 PLT_SsdpSender::SendSsdp(req,
314 NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"),
315 "upnp:rootdevice",
316 socket,
317 true,
318 &addr);
319 }
320
321 // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver
322 PLT_SsdpSender::SendSsdp(req,
323 NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType),
324 device->m_DeviceType,
325 socket,
326 true,
327 &addr);
328
329 // services
330 for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) {
331 // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver
332 PLT_SsdpSender::SendSsdp(req,
333 NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()),
334 device->m_Services[i]->GetServiceType(),
335 socket,
336 true,
337 &addr);
338 }
339
340 // uuid:device-UUID
341 PLT_SsdpSender::SendSsdp(req,
342 "uuid:" + device->m_UUID,
343 "uuid:" + device->m_UUID,
344 socket,
345 true,
346 &addr);
347
348
349 // embedded devices
350 for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) {
351 Announce(device->m_EmbeddedDevices[j].AsPointer(),
352 req,
353 socket,
354 byebye);
355 }
356
357 return res;
358 }
359
360 /*----------------------------------------------------------------------
361 | PLT_DeviceHost::ProcessHttpRequest
362 +---------------------------------------------------------------------*/
363 NPT_Result
364 PLT_DeviceHost::ProcessHttpRequest(NPT_HttpRequest& request,
365 const NPT_HttpRequestContext& context,
366 NPT_HttpResponse& response)
367 {
368 // get the address of who sent us some data back*/
369 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
370 NPT_String method = request.GetMethod();
371 NPT_String protocol = request.GetProtocol();
372
373 if (method.Compare("POST") == 0) {
374 return ProcessHttpPostRequest(request, context, response);
375 } else if (method.Compare("SUBSCRIBE") == 0 || method.Compare("UNSUBSCRIBE") == 0) {
376 return ProcessHttpSubscriberRequest(request, context, response);
377 } else if (method.Compare("GET") == 0) {
378 if (request.GetUrl().GetPath() == m_URLDescription.GetPath()) {
379 return ProcessGetDescription(request, context, response);
380 }
381 }
382
383 response.SetStatus(405, "Bad Request");
384 return NPT_SUCCESS;
385 }
386
387 /*----------------------------------------------------------------------
388 | PLT_DeviceHost::ProcessGetDescription
389 +---------------------------------------------------------------------*/
390 NPT_Result
391 PLT_DeviceHost::ProcessGetDescription(NPT_HttpRequest& /*request*/,
392 const NPT_HttpRequestContext& /*context*/,
393 NPT_HttpResponse& response)
394 {
395 NPT_String doc;
396 NPT_CHECK_FATAL(GetDescription(doc));
397 PLT_HttpHelper::SetBody(response, doc);
398 PLT_HttpHelper::SetContentType(response, "text/xml");
399 return NPT_SUCCESS;
400 }
401
402 /*----------------------------------------------------------------------
403 | PLT_DeviceHost::ProcessPostRequest
404 +---------------------------------------------------------------------*/
405 NPT_Result
406 PLT_DeviceHost::ProcessHttpPostRequest(NPT_HttpRequest& request,
407 const NPT_HttpRequestContext& context,
408 NPT_HttpResponse& response)
409 {
410 NPT_Result res;
411 NPT_String service_type;
412 NPT_String str;
413 NPT_XmlElementNode* xml = NULL;
414 NPT_String soap_action_header;
415 PLT_Service* service;
416 NPT_XmlElementNode* soap_body;
417 NPT_XmlElementNode* soap_action;
418 const NPT_String* attr;
419 PLT_ActionDesc* action_desc;
420 PLT_ActionReference action;
421 NPT_MemoryStreamReference resp(new NPT_MemoryStream);
422 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
423 NPT_String method = request.GetMethod();
424 NPT_String url = request.GetUrl().ToRequestString(true);
425 NPT_String protocol = request.GetProtocol();
426
427 if (NPT_FAILED(FindServiceByControlURI(url, service)))
428 goto bad_request;
429
430 if (!request.GetHeaders().GetHeaderValue("SOAPAction"))
431 goto bad_request;
432
433 // extract the soap action name from the header
434 soap_action_header = *request.GetHeaders().GetHeaderValue("SOAPAction");
435 soap_action_header.TrimLeft('"');
436 soap_action_header.TrimRight('"');
437 char prefix[200];
438 char soap_action_name[100];
439 int ret;
440 //FIXME: no sscanf
441 ret = sscanf(soap_action_header, "%[^#]#%s",
442 prefix,
443 soap_action_name);
444 if (ret != 2)
445 goto bad_request;
446
447 // read the xml body and parse it
448 if (NPT_FAILED(PLT_HttpHelper::ParseBody(request, xml)))
449 goto bad_request;
450
451 // check envelope
452 if (xml->GetTag().Compare("Envelope", true))
453 goto bad_request;
454
455 // check namespace
456 if (!xml->GetNamespace() || xml->GetNamespace()->Compare("http://schemas.xmlsoap.org/soap/envelope/"))
457 goto bad_request;
458
459 // check encoding
460 attr = xml->GetAttribute("encodingStyle", "http://schemas.xmlsoap.org/soap/envelope/");
461 if (!attr || attr->Compare("http://schemas.xmlsoap.org/soap/encoding/"))
462 goto bad_request;
463
464 // read action
465 soap_body = PLT_XmlHelper::GetChild(xml, "Body");
466 if (soap_body == NULL)
467 goto bad_request;
468
469 PLT_XmlHelper::GetChild(soap_body, soap_action);
470 if (soap_action == NULL)
471 goto bad_request;
472
473 // verify action name is identical to SOAPACTION header*/
474 if (soap_action->GetTag().Compare(soap_action_name, true))
475 goto bad_request;
476
477 // verify namespace
478 if (!soap_action->GetNamespace() || soap_action->GetNamespace()->Compare(service->GetServiceType()))
479 goto bad_request;
480
481 // create a buffer for our response body and call the service
482 if ((action_desc = service->FindActionDesc(soap_action_name)) == NULL) {
483 res = NPT_FAILURE;
484 // create a bastard soap response
485 PLT_Action::FormatSoapError(401, "Invalid Action", *resp);
486 goto error;
487 }
488
489 // create an action object
490 action = new PLT_Action(action_desc);
491
492 // read all the arguments if any
493 for (NPT_List<NPT_XmlNode*>::Iterator args = soap_action->GetChildren().GetFirstItem(); args; args++) {
494 NPT_XmlElementNode* child = (*args)->AsElementNode();
495 if (!child) continue;
496
497 // Total HACK for xbox360 upnp uncompliance!
498 NPT_String name = child->GetTag();
499 if (action_desc->GetName() == "Browse" && name == "ContainerID") {
500 name = "ObjectID";
501 }
502
503 res = action->SetArgumentValue(
504 name,
505 child->GetText()?*child->GetText():"");
506
507 if (NPT_FAILED(res)) {
508 // FIXME: incorrect upnp error?
509 PLT_Action::FormatSoapError(401, "Invalid Action", *resp);
510 goto error;
511 }
512 }
513
514 if (NPT_FAILED(action->VerifyArguments(true))) {
515 action->SetError(402, "Invalid Args");
516 goto error;
517 }
518
519 // call the virtual function, it's all good
520 // set the error in case the it wasn't done
521 if (NPT_FAILED(OnAction(action, PLT_HttpRequestContext(request, context)))) {
522 goto error;
523 }
524
525 // create the soap response now
526 action->FormatSoapResponse(*resp);
527 goto done;
528
529 error:
530 if (!action.IsNull() && action->GetErrorCode() == 0) {
531 action->SetError(501, "Action Failed");
532 action->FormatSoapResponse(*resp);
533 }
534 response.SetStatus(500, "Internal Server Error");
535
536 done:
537 //args.Apply(NPT_ObjectDeleter<PLT_Argument>());
538
539 NPT_LargeSize resp_body_size;
540 if (NPT_SUCCEEDED(resp->GetAvailable(resp_body_size))) {
541 PLT_HttpHelper::SetContentType(response, "text/xml; charset=\"utf-8\"");
542 response.GetHeaders().SetHeader("Ext", ""); // should only be for M-POST but oh well
543 NPT_InputStreamReference input = resp;
544 PLT_HttpHelper::SetBody(response, input);
545 }
546
547 delete xml;
548 return NPT_SUCCESS;
549
550 bad_request:
551 delete xml;
552 response.SetStatus(400, "Bad Request");
553 return NPT_SUCCESS;
554 }
555
556 /*----------------------------------------------------------------------
557 | PLT_DeviceHost::ProcessHttpSubscriberRequest
558 +---------------------------------------------------------------------*/
559 NPT_Result
560 PLT_DeviceHost::ProcessHttpSubscriberRequest(NPT_HttpRequest& request,
561 const NPT_HttpRequestContext& context,
562 NPT_HttpResponse& response)
563 {
564 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
565 NPT_String method = request.GetMethod();
566 NPT_String url = request.GetUrl().ToRequestString(true);
567 NPT_String protocol = request.GetProtocol();
568
569 const NPT_String* nt = PLT_UPnPMessageHelper::GetNT(request);
570 const NPT_String* callback_urls = PLT_UPnPMessageHelper::GetCallbacks(request);
571 const NPT_String* sid = PLT_UPnPMessageHelper::GetSID(request);
572
573 PLT_Service* service;
574 if (NPT_FAILED(FindServiceByEventSubURI(url, service))) {
575 goto cleanup;
576 }
577
578 if (method.Compare("SUBSCRIBE") == 0) {
579 // Do we have a sid ?
580 if (sid) {
581 // make sure we don't have a callback nor a nt
582 if (nt || callback_urls) {
583 goto cleanup;
584 }
585
586 NPT_Int32 timeout;
587 if (NPT_FAILED(PLT_UPnPMessageHelper::GetTimeOut(request, timeout))) {
588 timeout = 1800;
589 }
590 // subscription renewed
591 // send the info to the service
592 service->ProcessRenewSubscription(context.GetLocalAddress(),
593 *sid,
594 timeout,
595 response);
596 return NPT_SUCCESS;
597 } else {
598 // new subscription ?
599 // verify nt is present and valid
600 if (!nt || nt->Compare("upnp:event", true)) {
601 response.SetStatus(412, "Precondition failed");
602 return NPT_FAILURE;
603 }
604 // verify callback is present
605 if (!callback_urls) {
606 response.SetStatus(412, "Precondition failed");
607 return NPT_FAILURE;
608 }
609
610 NPT_Int32 timeout;
611 if (NPT_FAILED(PLT_UPnPMessageHelper::GetTimeOut(request, timeout))) {
612 timeout = 1800;
613 }
614
615 // send the info to the service
616 service->ProcessNewSubscription(&m_TaskManager,
617 context.GetLocalAddress(),
618 *callback_urls,
619 timeout,
620 response);
621 return NPT_SUCCESS;
622 }
623 } else if (method.Compare("UNSUBSCRIBE") == 0) {
624 // Do we have a sid ?
625 if (sid && sid->GetLength() > 0) {
626 // make sure we don't have a callback nor a nt
627 if (nt || callback_urls) {
628 goto cleanup;
629 }
630
631 // subscription cancelled
632 // send the info to the service
633 service->ProcessCancelSubscription(context.GetLocalAddress(),
634 *sid,
635 response);
636 return NPT_SUCCESS;
637 }
638 }
639
640 cleanup:
641 response.SetStatus(405, "Bad Request");
642 return NPT_FAILURE;
643 }
644
645 /*----------------------------------------------------------------------
646 | PLT_DeviceHost::OnSsdpPacket
647 +---------------------------------------------------------------------*/
648 NPT_Result
649 PLT_DeviceHost::OnSsdpPacket(NPT_HttpRequest& request,
650 const NPT_HttpRequestContext& context)
651 {
652 return ProcessSsdpSearchRequest(request, context);
653 }
654
655 /*----------------------------------------------------------------------
656 | PLT_DeviceHost::ProcessSsdpSearchRequest
657 +---------------------------------------------------------------------*/
658 NPT_Result
659 PLT_DeviceHost::ProcessSsdpSearchRequest(NPT_HttpRequest& request,
660 const NPT_HttpRequestContext& context)
661 {
662 // get the address of who sent us some data back*/
663 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
664 NPT_String method = request.GetMethod();
665 NPT_String url = request.GetUrl().ToRequestString(true);
666 NPT_String protocol = request.GetProtocol();
667
668 if (method.Compare("M-SEARCH") == 0) {
669 NPT_LOG_INFO_1("Received M-SEARCH from %s", (const char*) ip_address);
670 PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, &request);
671
672 if (url.Compare("*") || protocol.Compare("HTTP/1.1"))
673 return NPT_FAILURE;
674
675 const NPT_String* st = PLT_UPnPMessageHelper::GetST(request);
676 NPT_CHECK_POINTER_SEVERE(st);
677
678 const NPT_String* man = PLT_UPnPMessageHelper::GetMAN(request);
679 if (!man || man->Compare("\"ssdp:discover\"", true))
680 return NPT_FAILURE;
681
682 NPT_UInt32 mx;
683 if (NPT_FAILED(PLT_UPnPMessageHelper::GetMX(request, mx)))
684 return NPT_FAILURE;
685
686 // create a task to respond to the request
687 NPT_TimeInterval timer((mx==0)?0:((int)(0 + ((unsigned short)NPT_System::GetRandomInteger() % ((mx>10)?10:mx)))), 0);
688 PLT_SsdpDeviceSearchResponseTask* task = new PLT_SsdpDeviceSearchResponseTask(this, context.GetRemoteAddress(), *st);
689 m_TaskManager.StartTask(task, &timer);
690 return NPT_SUCCESS;
691 }
692
693 return NPT_FAILURE;
694 }
695
696 /*----------------------------------------------------------------------
697 | PLT_DeviceHost::SendSsdpSearchResponse
698 +---------------------------------------------------------------------*/
699 NPT_Result
700 PLT_DeviceHost::SendSsdpSearchResponse(PLT_DeviceData* device,
701 NPT_HttpResponse& response,
702 NPT_UdpSocket& socket,
703 const char* st,
704 const NPT_SocketAddress* addr /* = NULL */)
705 {
706 NPT_LOG_INFO_1("Responding to a M-SEARCH request for %s", st);
707
708 // ssdp:all or upnp:rootdevice
709 if (NPT_String::Compare(st, "ssdp:all") == 0 || NPT_String::Compare(st, "upnp:rootdevice") == 0) {
710 if (device->m_ParentUUID.IsEmpty()) {
711 // upnp:rootdevice
712 PLT_SsdpSender::SendSsdp(response,
713 NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"),
714 "upnp:rootdevice",
715 socket,
716 false,
717 addr);
718 }
719 }
720
721 // uuid:device-UUID
722 if (NPT_String::Compare(st, "ssdp:all", false) == 0 || NPT_String::Compare(st, "uuid:" + device->m_UUID, false) == 0) {
723 // uuid:device-UUID
724 PLT_SsdpSender::SendSsdp(response,
725 "uuid:" + device->m_UUID,
726 "uuid:" + device->m_UUID,
727 socket,
728 false,
729 addr);
730 }
731
732 // urn:schemas-upnp-org:device:deviceType:ver
733 if (NPT_String::Compare(st, "ssdp:all", false) == 0 || NPT_String::Compare(st, device->m_DeviceType, false) == 0) {
734 // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver
735 PLT_SsdpSender::SendSsdp(response,
736 NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType),
737 device->m_DeviceType,
738 socket,
739 false,
740 addr);
741 }
742
743 // services
744 for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) {
745 if (NPT_String::Compare(st, "ssdp:all", false) == 0 || NPT_String::Compare(st, device->m_Services[i]->GetServiceType(), false) == 0) {
746 // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver
747 PLT_SsdpSender::SendSsdp(response,
748 NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()),
749 device->m_Services[i]->GetServiceType(),
750 socket,
751 false,
752 addr);
753 }
754 }
755
756 // embedded devices
757 for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) {
758 SendSsdpSearchResponse(device->m_EmbeddedDevices[j].AsPointer(), response, socket, st, addr);
759 }
760
761 return NPT_SUCCESS;
762 }
763
764 /*----------------------------------------------------------------------
765 | PLT_DeviceHost::OnAction
766 +---------------------------------------------------------------------*/
767 NPT_Result
768 PLT_DeviceHost::OnAction(PLT_ActionReference& action,
769 const PLT_HttpRequestContext& context)
770 {
771 NPT_COMPILER_UNUSED(context);
772 action->SetError(401, "Invalid Action");
773 return NPT_FAILURE;
774 }
775