Merge pull request #25959 from neo1973/TagLib_deprecation_warnings
[xbmc.git] / lib / libUPnP / Platinum / Source / Core / PltDeviceHost.cpp
blob6f5f09f9cfbead6bcf813304a5d987584f64a765
1 /*****************************************************************
3 | Platinum - Device Host
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
33 ****************************************************************/
35 /*----------------------------------------------------------------------
36 | includes
37 +---------------------------------------------------------------------*/
38 #include "PltService.h"
39 #include "PltDeviceHost.h"
40 #include "PltUPnP.h"
41 #include "PltUtilities.h"
42 #include "PltSsdp.h"
43 #include "PltHttpServer.h"
44 #include "PltVersion.h"
46 NPT_SET_LOCAL_LOGGER("platinum.core.devicehost")
48 /*----------------------------------------------------------------------
49 | externals
50 +---------------------------------------------------------------------*/
51 extern NPT_UInt8 Platinum_120x120_jpg[16096];
52 extern NPT_UInt8 Platinum_120x120_png[26577];
53 extern NPT_UInt8 Platinum_48x48_jpg[3041];
54 extern NPT_UInt8 Platinum_48x48_png[4681];
56 /*----------------------------------------------------------------------
57 | PLT_DeviceHost::PLT_DeviceHost
58 +---------------------------------------------------------------------*/
59 PLT_DeviceHost::PLT_DeviceHost(const char* description_path /* = "/" */,
60 const char* uuid /* = "" */,
61 const char* device_type /* = "" */,
62 const char* friendly_name /* = "" */,
63 bool show_ip /* = false */,
64 NPT_UInt16 port /* = 0 */,
65 bool port_rebind /* = false */) :
66 PLT_DeviceData(NPT_HttpUrl(NULL, 0, description_path),
67 uuid,
68 *PLT_Constants::GetInstance().GetDefaultDeviceLease(),
69 device_type,
70 friendly_name),
71 m_TaskManager(NULL),
72 m_HttpServer(NULL),
73 m_ExtraBroascast(false),
74 m_Port(port),
75 m_PortRebind(port_rebind),
76 m_ByeByeFirst(true),
77 m_Started(false)
79 if (show_ip) {
80 NPT_List<NPT_IpAddress> ips;
81 PLT_UPnPMessageHelper::GetIPAddresses(ips);
82 if (ips.GetItemCount()) {
83 m_FriendlyName += " (" + ips.GetFirstItem()->ToString() + ")";
88 /*----------------------------------------------------------------------
89 | PLT_DeviceHost::~PLT_DeviceHost
90 +---------------------------------------------------------------------*/
91 PLT_DeviceHost::~PLT_DeviceHost()
95 /*----------------------------------------------------------------------
96 | PLT_DeviceHost::AddIcon
97 +---------------------------------------------------------------------*/
98 NPT_Result
99 PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon,
100 const char* fileroot,
101 const char* urlroot /* = "/" */)
103 // verify the url of the icon starts with the url root
104 if (!icon.m_UrlPath.StartsWith(urlroot)) return NPT_ERROR_INVALID_PARAMETERS;
106 NPT_HttpFileRequestHandler* icon_handler = new NPT_HttpFileRequestHandler(urlroot, fileroot);
107 m_HttpServer->AddRequestHandler(icon_handler, icon.m_UrlPath, false, true);
108 return m_Icons.Add(icon);
111 /*----------------------------------------------------------------------
112 | PLT_DeviceHost::AddIcon
113 +---------------------------------------------------------------------*/
114 NPT_Result
115 PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon,
116 const void* data,
117 NPT_Size size,
118 bool copy /* = true */)
120 NPT_HttpStaticRequestHandler* icon_handler =
121 new NPT_HttpStaticRequestHandler(
122 data,
123 size,
124 icon.m_MimeType,
125 copy);
126 m_HttpServer->AddRequestHandler(icon_handler, icon.m_UrlPath, false, true);
127 return m_Icons.Add(icon);
130 /*----------------------------------------------------------------------
131 | PLT_DeviceHost::SetupIcons
132 +---------------------------------------------------------------------*/
133 NPT_Result
134 PLT_DeviceHost::SetupIcons()
136 /*if (m_Icons.GetItemCount() == 0) {
137 AddIcon(
138 PLT_DeviceIcon("image/jpeg", 120, 120, 24, "/images/platinum-120x120.jpg"),
139 Platinum_120x120_jpg, sizeof(Platinum_120x120_jpg), false);
140 AddIcon(
141 PLT_DeviceIcon("image/jpeg", 48, 48, 24, "/images/platinum-48x48.jpg"),
142 Platinum_48x48_jpg, sizeof(Platinum_48x48_jpg), false);
143 AddIcon(
144 PLT_DeviceIcon("image/png", 120, 120, 24, "/images/platinum-120x120.png"),
145 Platinum_120x120_png, sizeof(Platinum_120x120_png), false);
146 AddIcon(
147 PLT_DeviceIcon("image/png", 48, 48, 24, "/images/platinum-48x48.png"),
148 Platinum_48x48_png, sizeof(Platinum_48x48_png), false);
150 return NPT_SUCCESS;
153 /*----------------------------------------------------------------------
154 | PLT_DeviceHost::SetupDevice
155 +---------------------------------------------------------------------*/
156 NPT_Result
157 PLT_DeviceHost::SetupDevice()
159 NPT_CHECK_FATAL(SetupServices());
160 NPT_CHECK_WARNING(SetupIcons());
161 return NPT_SUCCESS;
164 /*----------------------------------------------------------------------
165 | PLT_DeviceHost::Start
166 +---------------------------------------------------------------------*/
167 NPT_Result
168 PLT_DeviceHost::Start(PLT_SsdpListenTask* task)
170 NPT_Result result;
172 if (m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
174 // setup
175 m_TaskManager = new PLT_TaskManager();
176 m_HttpServer = new PLT_HttpServer(NPT_IpAddress::Any, m_Port, m_PortRebind, 100); // limit to 100 clients max
177 if (NPT_FAILED(result = m_HttpServer->Start())) {
178 m_TaskManager = NULL;
179 m_HttpServer = NULL;
180 NPT_CHECK_FATAL(result);
183 // read back assigned port in case we passed 0 to randomly select one
184 m_Port = m_HttpServer->GetPort();
185 m_URLDescription.SetPort(m_Port);
187 // callback to initialize the device
188 if (NPT_FAILED(result = SetupDevice())) {
189 m_TaskManager = NULL;
190 m_HttpServer = NULL;
191 NPT_CHECK_FATAL(result);
194 // all other requests including description document
195 // and service control are dynamically handled
196 m_HttpServer->AddRequestHandler(new PLT_HttpRequestHandler(this), "/", true, true);
198 // we should not advertise right away
199 // spec says randomly less than 100ms
200 NPT_TimeInterval delay(((NPT_Int64)NPT_System::GetRandomInteger()%100)*1000000);
202 // calculate when we should send another announcement
203 // we announce a bit before half way through leasetime to make sure
204 // clients don't expire us.
205 NPT_Size leaseTime = (NPT_Size)GetLeaseTime().ToSeconds();
206 NPT_TimeInterval repeat;
207 repeat.SetSeconds(leaseTime?(int)((leaseTime >> 1) - 10):30);
209 PLT_ThreadTask* announce_task = new PLT_SsdpDeviceAnnounceTask(
210 this,
211 repeat,
212 m_ByeByeFirst,
213 m_ExtraBroascast);
214 m_TaskManager->StartTask(announce_task, &delay);
216 // register ourselves as a listener for SSDP search requests
217 task->AddListener(this);
219 m_Started = true;
220 return NPT_SUCCESS;
223 /*----------------------------------------------------------------------
224 | PLT_DeviceHost::Stop
225 +---------------------------------------------------------------------*/
226 NPT_Result
227 PLT_DeviceHost::Stop(PLT_SsdpListenTask* task)
229 if (!m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
231 // mark immediately we're stopping
232 m_Started = false;
234 // unregister ourselves as a listener for ssdp requests
235 task->RemoveListener(this);
237 // remove all our running tasks
238 m_TaskManager->Abort();
240 // stop our internal http server
241 m_HttpServer->Stop();
243 // announce we're leaving
244 NPT_List<NPT_NetworkInterface*> if_list;
245 PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list, true);
246 if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(this, PLT_ANNOUNCETYPE_BYEBYE, m_ExtraBroascast));
247 if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>());
249 // Cleanup all services and embedded devices
250 PLT_DeviceData::Cleanup();
252 m_HttpServer = NULL;
253 m_TaskManager = NULL;
255 return NPT_SUCCESS;
258 /*----------------------------------------------------------------------
259 | PLT_DeviceHost::Announce
260 +---------------------------------------------------------------------*/
261 NPT_Result
262 PLT_DeviceHost::Announce(PLT_DeviceData* device,
263 NPT_HttpRequest& req,
264 NPT_UdpSocket& socket,
265 PLT_SsdpAnnounceType type)
267 NPT_Result res = NPT_SUCCESS;
269 // target address
270 NPT_IpAddress ip;
271 NPT_CHECK_FATAL(ip.ResolveName(req.GetUrl().GetHost()));
272 NPT_SocketAddress addr(ip, req.GetUrl().GetPort());
274 // // UPnP 1.1 BOOTID.UPNP.ORG header
275 // PLT_UPnPMessageHelper::SetBootId(req, device->m_BootId);
277 // // UPnP 1.1 CONFIGID.UPNP.ORG header
278 // if (device->m_ConfigId > 0) {
279 // PLT_UPnPMessageHelper::SetConfigId(req, device->m_ConfigId);
280 // }
282 // NTS header
283 NPT_String nts;
284 switch (type) {
285 case PLT_ANNOUNCETYPE_ALIVE:
286 nts = "ssdp:alive";
287 PLT_UPnPMessageHelper::SetLeaseTime(req, device->GetLeaseTime());
288 PLT_UPnPMessageHelper::SetServer(req, PLT_HTTP_DEFAULT_SERVER, false);
289 break;
291 case PLT_ANNOUNCETYPE_BYEBYE:
292 nts = "ssdp:byebye";
293 break;
295 case PLT_ANNOUNCETYPE_UPDATE:
296 nts = "ssdp:update";
297 // update requires valid UPNP 1.1 NEXTBOOTID.UPNP.ORG Header
298 if (device->m_NextBootId == 0) {
299 NPT_CHECK_FATAL(NPT_ERROR_INTERNAL);
301 PLT_UPnPMessageHelper::SetNextBootId(req, device->m_NextBootId);
302 break;
304 default:
305 break;
307 PLT_UPnPMessageHelper::SetNTS(req, nts);
309 NPT_LOG_FINER_3("Sending SSDP NOTIFY (%s) Request to %s (%s)", nts.GetChars(),
310 (const char*)req.GetUrl().ToString(),
311 (const char*)(PLT_UPnPMessageHelper::GetLocation(req)
312 ? PLT_UPnPMessageHelper::GetLocation(req)->GetChars()
313 : ""));
315 // upnp:rootdevice
316 if (device->m_ParentUUID.IsEmpty()) {
317 PLT_SsdpSender::SendSsdp(req,
318 NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"),
319 "upnp:rootdevice",
320 socket,
321 true,
322 &addr);
325 // on byebye, don't sleep otherwise it hangs when we stop upnp
326 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
327 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
330 // uuid:device-UUID
331 PLT_SsdpSender::SendSsdp(req,
332 "uuid:" + device->m_UUID,
333 "uuid:" + device->m_UUID,
334 socket,
335 true,
336 &addr);
338 // on byebye, don't sleep otherwise it hangs when we stop upnp
339 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
340 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
343 // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver
344 PLT_SsdpSender::SendSsdp(req,
345 NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType),
346 device->m_DeviceType,
347 socket,
348 true,
349 &addr);
351 // on byebye, don't sleep otherwise it hangs when we stop upnp
352 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
353 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
356 // services
357 for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) {
358 // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver
359 PLT_SsdpSender::SendSsdp(req,
360 NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()),
361 device->m_Services[i]->GetServiceType(),
362 socket,
363 true,
364 &addr);
366 // on byebye, don't sleep otherwise it hangs when we stop upnp
367 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
368 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
372 // embedded devices
373 for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) {
374 Announce(device->m_EmbeddedDevices[j].AsPointer(),
375 req,
376 socket,
377 type);
380 return res;
383 /*----------------------------------------------------------------------
384 | PLT_DeviceHost::SetupResponse
385 +---------------------------------------------------------------------*/
386 NPT_Result
387 PLT_DeviceHost::SetupResponse(NPT_HttpRequest& request,
388 const NPT_HttpRequestContext& context,
389 NPT_HttpResponse& response)
391 // get the address of who sent us some data back*/
392 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
393 NPT_String method = request.GetMethod();
394 NPT_String protocol = request.GetProtocol();
396 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINER, "PLT_DeviceHost::SetupResponse:", &request);
398 if (method.Compare("POST") == 0) {
399 return ProcessHttpPostRequest(request, context, response);
400 } else if (method.Compare("SUBSCRIBE") == 0 || method.Compare("UNSUBSCRIBE") == 0) {
401 return ProcessHttpSubscriberRequest(request, context, response);
402 } else if (method.Compare("GET") == 0 || method.Compare("HEAD") == 0) {
403 // process SCPD requests
404 PLT_Service* service;
405 if (NPT_SUCCEEDED(FindServiceBySCPDURL(request.GetUrl().ToRequestString(), service, true))) {
406 return ProcessGetSCPD(service, request, context, response);
409 // process Description document requests
410 if (request.GetUrl().GetPath() == m_URLDescription.GetPath()) {
411 return ProcessGetDescription(request, context, response);
414 // process other requests
415 return ProcessHttpGetRequest(request, context, response);
418 response.SetStatus(405, "Bad Request");
419 return NPT_SUCCESS;
422 /*----------------------------------------------------------------------
423 | PLT_DeviceHost::ProcessHttpGetRequest
424 +---------------------------------------------------------------------*/
425 NPT_Result
426 PLT_DeviceHost::ProcessHttpGetRequest(NPT_HttpRequest& request,
427 const NPT_HttpRequestContext& context,
428 NPT_HttpResponse& response)
430 NPT_COMPILER_UNUSED(request);
431 NPT_COMPILER_UNUSED(context);
432 NPT_COMPILER_UNUSED(response);
434 return NPT_ERROR_NO_SUCH_ITEM;
437 /*----------------------------------------------------------------------
438 | PLT_DeviceHost::ProcessGetDescription
439 +---------------------------------------------------------------------*/
440 NPT_Result
441 PLT_DeviceHost::ProcessGetDescription(NPT_HttpRequest& /*request*/,
442 const NPT_HttpRequestContext& context,
443 NPT_HttpResponse& response)
445 NPT_COMPILER_UNUSED(context);
447 NPT_String doc;
448 NPT_CHECK_FATAL(GetDescription(doc));
449 NPT_LOG_FINEST_2("Returning description to %s: %s",
450 (const char*)context.GetRemoteAddress().GetIpAddress().ToString(),
451 (const char*)doc);
453 NPT_HttpEntity* entity;
454 PLT_HttpHelper::SetBody(response, doc, &entity);
455 entity->SetContentType("text/xml; charset=\"utf-8\"");
456 return NPT_SUCCESS;
459 /*----------------------------------------------------------------------
460 | PLT_DeviceHost::ProcessGetSCPD
461 +---------------------------------------------------------------------*/
462 NPT_Result
463 PLT_DeviceHost::ProcessGetSCPD(PLT_Service* service,
464 NPT_HttpRequest& /*request*/,
465 const NPT_HttpRequestContext& context,
466 NPT_HttpResponse& response)
468 NPT_COMPILER_UNUSED(context);
469 NPT_CHECK_POINTER_FATAL(service);
471 NPT_String doc;
472 NPT_CHECK_FATAL(service->GetSCPDXML(doc));
473 NPT_LOG_FINEST_2("Returning SCPD to %s: %s",
474 (const char*)context.GetRemoteAddress().GetIpAddress().ToString(),
475 (const char*)doc);
477 NPT_HttpEntity* entity;
478 PLT_HttpHelper::SetBody(response, doc, &entity);
479 entity->SetContentType("text/xml; charset=\"utf-8\"");
480 return NPT_SUCCESS;
483 /*----------------------------------------------------------------------
484 | PLT_DeviceHost::ProcessPostRequest
485 +---------------------------------------------------------------------*/
486 NPT_Result
487 PLT_DeviceHost::ProcessHttpPostRequest(NPT_HttpRequest& request,
488 const NPT_HttpRequestContext& context,
489 NPT_HttpResponse& response)
491 NPT_Result res;
492 NPT_String service_type;
493 NPT_String str;
494 NPT_XmlElementNode* xml = NULL;
495 NPT_String soap_action_header;
496 PLT_Service* service;
497 NPT_XmlElementNode* soap_body;
498 NPT_XmlElementNode* soap_action;
499 PLT_ActionDesc* action_desc;
500 PLT_ActionReference action;
501 NPT_MemoryStreamReference resp(new NPT_MemoryStream);
502 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
503 NPT_String method = request.GetMethod();
504 NPT_String url = request.GetUrl().ToRequestString();
505 NPT_String protocol = request.GetProtocol();
506 NPT_List<NPT_String> components;
507 NPT_String soap_action_name;
509 #if defined(PLATINUM_UPNP_SPECS_STRICT)
510 const NPT_String* attr;
511 #endif
513 if (NPT_FAILED(FindServiceByControlURL(url, service, true)))
514 goto bad_request_find_service;
516 if (!request.GetHeaders().GetHeaderValue("SOAPAction"))
517 goto bad_request_soap_header_value;
519 // extract the soap action name from the header
520 soap_action_header = *request.GetHeaders().GetHeaderValue("SOAPAction");
521 soap_action_header.TrimLeft('"');
522 soap_action_header.TrimRight('"');
524 components = soap_action_header.Split("#");
525 if (components.GetItemCount() != 2)
526 goto bad_request_soap_action_header;
528 soap_action_name = *components.GetItem(1);
530 // read the xml body and parse it
531 if (NPT_FAILED(PLT_HttpHelper::ParseBody(request, xml)))
532 goto bad_request_body_parse_error;
534 // check envelope
535 if (xml->GetTag().Compare("Envelope", true))
536 goto bad_request_no_envelope;
538 #if defined(PLATINUM_UPNP_SPECS_STRICT)
539 // check namespace
540 if (!xml->GetNamespace() || xml->GetNamespace()->Compare("http://schemas.xmlsoap.org/soap/envelope/"))
541 goto bad_request_upnp_not_strict;
543 // check encoding
544 attr = xml->GetAttribute("encodingStyle", "http://schemas.xmlsoap.org/soap/envelope/");
545 if (!attr || attr->Compare("http://schemas.xmlsoap.org/soap/encoding/"))
546 goto bad_request_bad_encoding;
547 #endif
549 // read action
550 soap_body = PLT_XmlHelper::GetChild(xml, "Body");
551 if (soap_body == NULL)
552 goto bad_request_soap_body;
554 PLT_XmlHelper::GetChild(soap_body, soap_action);
555 if (soap_action == NULL)
556 goto bad_request_soap_action_body;
558 // verify action name is identical to SOAPACTION header*/
559 if (soap_action->GetTag().Compare(soap_action_name, true))
560 goto bad_request_action_mismatch;
562 // verify namespace
563 if (!soap_action->GetNamespace() || soap_action->GetNamespace()->Compare(service->GetServiceType()))
564 goto bad_request_bad_namespace;
566 // create a buffer for our response body and call the service
567 if ((action_desc = service->FindActionDesc(soap_action_name)) == NULL) {
568 // create a bastard soap response
569 PLT_Action::FormatSoapError(401, "Invalid Action", *resp);
570 goto error;
573 // create a new action object
574 action = new PLT_Action(*action_desc);
576 // read all the arguments if any
577 for (NPT_List<NPT_XmlNode*>::Iterator args = soap_action->GetChildren().GetFirstItem();
578 args;
579 args++) {
580 NPT_XmlElementNode* child = (*args)->AsElementNode();
581 if (!child) continue;
583 // Total HACK for xbox360 upnp uncompliance!
584 NPT_String name = child->GetTag();
585 if (action_desc->GetName() == "Browse" && name == "ContainerID") {
586 name = "ObjectID";
589 res = action->SetArgumentValue(name, child->GetText() ? child->GetText()->GetChars() : "");
591 // test if value was correct
592 if (res == NPT_ERROR_INVALID_PARAMETERS)
594 action->SetError(701, "Invalid Name");
595 goto error;
599 // verify all required arguments were passed
600 if (NPT_FAILED(action->VerifyArguments(true))) {
601 action->SetError(402, "Invalid or Missing Args");
602 goto error;
605 NPT_LOG_FINE_2("Processing action \"%s\" from %s",
606 (const char*)action->GetActionDesc().GetName(),
607 (const char*)context.GetRemoteAddress().GetIpAddress().ToString());
609 // call the virtual function, it's all good
610 if (NPT_FAILED(OnAction(action, PLT_HttpRequestContext(request, context)))) {
611 goto error;
614 // create the soap response now
615 action->FormatSoapResponse(*resp);
616 goto done;
618 error:
619 if (!action.IsNull()) {
620 // set the error in case it wasn't done already
621 if (action->GetErrorCode() == 0) {
622 action->SetError(501, "Action Failed");
624 NPT_LOG_WARNING_3("Error while processing action %s: %d %s",
625 (const char*)action->GetActionDesc().GetName(),
626 action->GetErrorCode(),
627 action->GetError());
629 action->FormatSoapResponse(*resp);
632 response.SetStatus(500, "Internal Server Error");
634 done:
635 NPT_LargeSize resp_body_size;
636 if (NPT_SUCCEEDED(resp->GetAvailable(resp_body_size))) {
637 NPT_HttpEntity* entity;
638 PLT_HttpHelper::SetBody(response,
639 (NPT_InputStreamReference)resp,
640 &entity);
641 entity->SetContentType("text/xml; charset=\"utf-8\"");
642 response.GetHeaders().SetHeader("Ext", ""); // should only be for M-POST but oh well
645 delete xml;
646 return NPT_SUCCESS;
648 bad_request:
649 // generic 500 now unused
650 response.SetStatus(500, "Bad Request");
651 goto bad_request_end;
653 bad_request_find_service:
654 response.SetStatus(500, "Bad Request: Service by URL");
655 goto bad_request_end;
657 bad_request_soap_header_value:
658 response.SetStatus(500, "Bad Request: SOAP Header");
659 goto bad_request_end;
661 bad_request_soap_action_header:
662 response.SetStatus(500, "Bad Request: SOAP Action in Header");
663 goto bad_request_end;
665 bad_request_body_parse_error:
666 response.SetStatus(500, "Bad Request: Error Parsing XML Body");
667 goto bad_request_end;
669 bad_request_no_envelope:
670 response.SetStatus(500, "Bad Request: SOAP Envelope");
671 goto bad_request_end;
673 #if defined(PLATINUM_UPNP_SPECS_STRICT)
674 bad_request_upnp_not_strict:
675 response.SetStatus(500, "Bad Request: SOAP not Strict");
676 goto bad_request_end;
678 bad_request_bad_encoding:
679 response.SetStatus(500, "Bad Request: SOAP Encoding");
680 goto bad_request_end;
681 #endif
683 bad_request_soap_body:
684 response.SetStatus(500, "Bad Request: SOAP Body");
685 goto bad_request_end;
687 bad_request_soap_action_body:
688 response.SetStatus(500, "Bad Request: SOAP Action in Body");
689 goto bad_request_end;
691 bad_request_action_mismatch:
692 response.SetStatus(500, "Bad Request: SOAP Action Mismatch");
693 goto bad_request_end;
695 bad_request_bad_namespace:
696 response.SetStatus(500, "Bad Request: Bad Namespace");
697 goto bad_request_end;
699 bad_request_end:
700 delete xml;
701 return NPT_SUCCESS;
704 /*----------------------------------------------------------------------
705 | PLT_DeviceHost::ProcessHttpSubscriberRequest
706 +---------------------------------------------------------------------*/
707 NPT_Result
708 PLT_DeviceHost::ProcessHttpSubscriberRequest(NPT_HttpRequest& request,
709 const NPT_HttpRequestContext& context,
710 NPT_HttpResponse& response)
712 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
713 NPT_String method = request.GetMethod();
714 NPT_String url = request.GetUrl().ToRequestString();
715 NPT_String protocol = request.GetProtocol();
717 const NPT_String* nt = PLT_UPnPMessageHelper::GetNT(request);
718 const NPT_String* callback_urls = PLT_UPnPMessageHelper::GetCallbacks(request);
719 const NPT_String* sid = PLT_UPnPMessageHelper::GetSID(request);
721 PLT_Service* service;
722 NPT_CHECK_LABEL_WARNING(FindServiceByEventSubURL(url, service, true), cleanup);
724 if (method.Compare("SUBSCRIBE") == 0) {
725 // Do we have a sid ?
726 if (sid) {
727 // make sure we don't have a callback nor a nt
728 if (nt || callback_urls) {
729 goto cleanup;
732 // default lease
733 NPT_Int32 timeout = *PLT_Constants::GetInstance().GetDefaultSubscribeLease().AsPointer();
735 // subscription renewed
736 // send the info to the service
737 service->ProcessRenewSubscription(context.GetLocalAddress(),
738 *sid,
739 timeout,
740 response);
741 return NPT_SUCCESS;
742 } else {
743 // new subscription ?
744 // verify nt is present and valid
745 if (!nt || nt->Compare("upnp:event", true)) {
746 response.SetStatus(412, "Precondition failed");
747 return NPT_SUCCESS;
749 // verify callback is present
750 if (!callback_urls) {
751 response.SetStatus(412, "Precondition failed");
752 return NPT_SUCCESS;
755 // default lease time
756 NPT_Int32 timeout = *PLT_Constants::GetInstance().GetDefaultSubscribeLease().AsPointer();
758 // send the info to the service
759 service->ProcessNewSubscription(m_TaskManager,
760 context.GetLocalAddress(),
761 *callback_urls,
762 timeout,
763 response);
764 return NPT_SUCCESS;
766 } else if (method.Compare("UNSUBSCRIBE") == 0) {
767 // Do we have a sid ?
768 if (sid && sid->GetLength() > 0) {
769 // make sure we don't have a callback nor a nt
770 if (nt || callback_urls) {
771 goto cleanup;
774 // subscription cancelled
775 // send the info to the service
776 service->ProcessCancelSubscription(context.GetLocalAddress(),
777 *sid,
778 response);
779 return NPT_SUCCESS;
782 response.SetStatus(412, "Precondition failed");
783 return NPT_SUCCESS;
786 cleanup:
787 response.SetStatus(400, "Bad Request");
788 return NPT_SUCCESS;
791 /*----------------------------------------------------------------------
792 | PLT_DeviceHost::OnSsdpPacket
793 +---------------------------------------------------------------------*/
794 NPT_Result
795 PLT_DeviceHost::OnSsdpPacket(const NPT_HttpRequest& request,
796 const NPT_HttpRequestContext& context)
798 // get the address of who sent us some data back*/
799 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
800 NPT_String method = request.GetMethod();
801 NPT_String url = request.GetUrl().ToRequestString(true);
802 NPT_String protocol = request.GetProtocol();
803 NPT_IpPort remote_port = context.GetRemoteAddress().GetPort();
804 const NPT_String* st = PLT_UPnPMessageHelper::GetST(request);
806 if (method.Compare("M-SEARCH") == 0) {
807 NPT_String prefix = NPT_String::Format("PLT_DeviceHost::OnSsdpPacket M-SEARCH for %s from %s:%d",
808 st?st->GetChars():"Unknown",
809 (const char*) ip_address, remote_port);
810 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINE, prefix, &request);
813 // DLNA 7.2.3.5 support
814 if (remote_port < 1024 || remote_port == 1900) {
815 NPT_LOG_INFO_2("Ignoring M-SEARCH from %s:%d (invalid source port)",
816 (const char*) ip_address,
817 remote_port);
818 return NPT_FAILURE;
822 NPT_CHECK_POINTER_SEVERE(st);
824 if (url.Compare("*") || protocol.Compare("HTTP/1.1"))
825 return NPT_FAILURE;
827 const NPT_String* man = PLT_UPnPMessageHelper::GetMAN(request);
828 if (!man || man->Compare("\"ssdp:discover\"", true))
829 return NPT_FAILURE;
831 NPT_UInt32 mx;
832 NPT_CHECK_SEVERE(PLT_UPnPMessageHelper::GetMX(request, mx));
834 // create a task to respond to the request
835 NPT_TimeInterval timer((mx==0)?0.:(double)(NPT_System::GetRandomInteger()%(mx>5?5:mx)));
836 PLT_SsdpDeviceSearchResponseTask* task = new PLT_SsdpDeviceSearchResponseTask(this, context.GetRemoteAddress(), *st);
837 m_TaskManager->StartTask(task, &timer);
838 return NPT_SUCCESS;
839 } else {
840 NPT_String prefix = NPT_String::Format("Ignoring %s request from %s:%d",
841 method.GetChars(),
842 (const char*) ip_address, remote_port);
843 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINE, prefix, &request);
846 return NPT_FAILURE;
849 /*----------------------------------------------------------------------
850 | PLT_DeviceHost::SendSsdpSearchResponse
851 +---------------------------------------------------------------------*/
852 NPT_Result
853 PLT_DeviceHost::SendSsdpSearchResponse(PLT_DeviceData* device,
854 NPT_HttpResponse& response,
855 NPT_UdpSocket& socket,
856 const char* st,
857 const NPT_SocketAddress* addr /* = NULL */)
859 // UPnP 1.1 BOOTID.UPNP.ORG header
860 PLT_UPnPMessageHelper::SetBootId(response, device->m_BootId);
862 // UPnP 1.1 CONFIGID.UPNP.ORG header
863 if (device->m_ConfigId > 0) {
864 PLT_UPnPMessageHelper::SetConfigId(response, device->m_ConfigId);
867 // ssdp:all or upnp:rootdevice
868 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
869 NPT_String::Compare(st, "upnp:rootdevice") == 0) {
871 if (device->m_ParentUUID.IsEmpty()) {
872 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
874 // upnp:rootdevice
875 PLT_SsdpSender::SendSsdp(response,
876 NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"),
877 "upnp:rootdevice",
878 socket,
879 false,
880 addr);
884 // uuid:device-UUID
885 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
886 NPT_String::Compare(st, (const char*)("uuid:" + device->m_UUID)) == 0) {
888 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
890 // uuid:device-UUID
891 PLT_SsdpSender::SendSsdp(response,
892 "uuid:" + device->m_UUID,
893 "uuid:" + device->m_UUID,
894 socket,
895 false,
896 addr);
899 // urn:schemas-upnp-org:device:deviceType:ver
900 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
901 NPT_String::Compare(st, (const char*)(device->m_DeviceType)) == 0) {
903 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
905 // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver
906 PLT_SsdpSender::SendSsdp(response,
907 NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType),
908 device->m_DeviceType,
909 socket,
910 false,
911 addr);
914 // services
915 for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) {
916 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
917 NPT_String::Compare(st, (const char*)(device->m_Services[i]->GetServiceType())) == 0) {
919 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
921 // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver
922 PLT_SsdpSender::SendSsdp(response,
923 NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()),
924 device->m_Services[i]->GetServiceType(),
925 socket,
926 false,
927 addr);
931 // embedded devices
932 for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) {
933 SendSsdpSearchResponse(device->m_EmbeddedDevices[j].AsPointer(),
934 response,
935 socket,
936 st,
937 addr);
940 return NPT_SUCCESS;
943 /*----------------------------------------------------------------------
944 | PLT_DeviceHost::OnAction
945 +---------------------------------------------------------------------*/
946 NPT_Result
947 PLT_DeviceHost::OnAction(PLT_ActionReference& action,
948 const PLT_HttpRequestContext& context)
950 NPT_COMPILER_UNUSED(context);
951 action->SetError(401, "Invalid Action");
952 return NPT_FAILURE;