1 /*****************************************************************
3 | Platinum - Device Host
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
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 /*----------------------------------------------------------------------
37 +---------------------------------------------------------------------*/
38 #include "PltService.h"
39 #include "PltDeviceHost.h"
41 #include "PltUtilities.h"
43 #include "PltHttpServer.h"
44 #include "PltVersion.h"
46 NPT_SET_LOCAL_LOGGER("platinum.core.devicehost")
48 /*----------------------------------------------------------------------
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
),
68 *PLT_Constants::GetInstance().GetDefaultDeviceLease(),
73 m_ExtraBroascast(false),
75 m_PortRebind(port_rebind
),
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 +---------------------------------------------------------------------*/
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 +---------------------------------------------------------------------*/
115 PLT_DeviceHost::AddIcon(const PLT_DeviceIcon
& icon
,
118 bool copy
/* = true */)
120 NPT_HttpStaticRequestHandler
* icon_handler
=
121 new NPT_HttpStaticRequestHandler(
126 m_HttpServer
->AddRequestHandler(icon_handler
, icon
.m_UrlPath
, false, true);
127 return m_Icons
.Add(icon
);
130 /*----------------------------------------------------------------------
131 | PLT_DeviceHost::SetupIcons
132 +---------------------------------------------------------------------*/
134 PLT_DeviceHost::SetupIcons()
136 /*if (m_Icons.GetItemCount() == 0) {
138 PLT_DeviceIcon("image/jpeg", 120, 120, 24, "/images/platinum-120x120.jpg"),
139 Platinum_120x120_jpg, sizeof(Platinum_120x120_jpg), false);
141 PLT_DeviceIcon("image/jpeg", 48, 48, 24, "/images/platinum-48x48.jpg"),
142 Platinum_48x48_jpg, sizeof(Platinum_48x48_jpg), false);
144 PLT_DeviceIcon("image/png", 120, 120, 24, "/images/platinum-120x120.png"),
145 Platinum_120x120_png, sizeof(Platinum_120x120_png), false);
147 PLT_DeviceIcon("image/png", 48, 48, 24, "/images/platinum-48x48.png"),
148 Platinum_48x48_png, sizeof(Platinum_48x48_png), false);
153 /*----------------------------------------------------------------------
154 | PLT_DeviceHost::SetupDevice
155 +---------------------------------------------------------------------*/
157 PLT_DeviceHost::SetupDevice()
159 NPT_CHECK_FATAL(SetupServices());
160 NPT_CHECK_WARNING(SetupIcons());
164 /*----------------------------------------------------------------------
165 | PLT_DeviceHost::Start
166 +---------------------------------------------------------------------*/
168 PLT_DeviceHost::Start(PLT_SsdpListenTask
* task
)
172 if (m_Started
) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE
);
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
;
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
;
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(
214 m_TaskManager
->StartTask(announce_task
, &delay
);
216 // register ourselves as a listener for SSDP search requests
217 task
->AddListener(this);
223 /*----------------------------------------------------------------------
224 | PLT_DeviceHost::Stop
225 +---------------------------------------------------------------------*/
227 PLT_DeviceHost::Stop(PLT_SsdpListenTask
* task
)
229 if (!m_Started
) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE
);
231 // mark immediately we're stopping
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();
253 m_TaskManager
= NULL
;
258 /*----------------------------------------------------------------------
259 | PLT_DeviceHost::Announce
260 +---------------------------------------------------------------------*/
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
;
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);
285 case PLT_ANNOUNCETYPE_ALIVE
:
287 PLT_UPnPMessageHelper::SetLeaseTime(req
, device
->GetLeaseTime());
288 PLT_UPnPMessageHelper::SetServer(req
, PLT_HTTP_DEFAULT_SERVER
, false);
291 case PLT_ANNOUNCETYPE_BYEBYE
:
295 case PLT_ANNOUNCETYPE_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
);
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()
316 if (device
->m_ParentUUID
.IsEmpty()) {
317 PLT_SsdpSender::SendSsdp(req
,
318 NPT_String("uuid:" + device
->m_UUID
+ "::upnp:rootdevice"),
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
));
331 PLT_SsdpSender::SendSsdp(req
,
332 "uuid:" + device
->m_UUID
,
333 "uuid:" + device
->m_UUID
,
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
,
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
));
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(),
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
));
373 for (int j
=0; j
< (int)device
->m_EmbeddedDevices
.GetItemCount(); j
++) {
374 Announce(device
->m_EmbeddedDevices
[j
].AsPointer(),
383 /*----------------------------------------------------------------------
384 | PLT_DeviceHost::SetupResponse
385 +---------------------------------------------------------------------*/
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");
422 /*----------------------------------------------------------------------
423 | PLT_DeviceHost::ProcessHttpGetRequest
424 +---------------------------------------------------------------------*/
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 +---------------------------------------------------------------------*/
441 PLT_DeviceHost::ProcessGetDescription(NPT_HttpRequest
& /*request*/,
442 const NPT_HttpRequestContext
& context
,
443 NPT_HttpResponse
& response
)
445 NPT_COMPILER_UNUSED(context
);
448 NPT_CHECK_FATAL(GetDescription(doc
));
449 NPT_LOG_FINEST_2("Returning description to %s: %s",
450 (const char*)context
.GetRemoteAddress().GetIpAddress().ToString(),
453 NPT_HttpEntity
* entity
;
454 PLT_HttpHelper::SetBody(response
, doc
, &entity
);
455 entity
->SetContentType("text/xml; charset=\"utf-8\"");
459 /*----------------------------------------------------------------------
460 | PLT_DeviceHost::ProcessGetSCPD
461 +---------------------------------------------------------------------*/
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
);
472 NPT_CHECK_FATAL(service
->GetSCPDXML(doc
));
473 NPT_LOG_FINEST_2("Returning SCPD to %s: %s",
474 (const char*)context
.GetRemoteAddress().GetIpAddress().ToString(),
477 NPT_HttpEntity
* entity
;
478 PLT_HttpHelper::SetBody(response
, doc
, &entity
);
479 entity
->SetContentType("text/xml; charset=\"utf-8\"");
483 /*----------------------------------------------------------------------
484 | PLT_DeviceHost::ProcessPostRequest
485 +---------------------------------------------------------------------*/
487 PLT_DeviceHost::ProcessHttpPostRequest(NPT_HttpRequest
& request
,
488 const NPT_HttpRequestContext
& context
,
489 NPT_HttpResponse
& response
)
492 NPT_String service_type
;
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
;
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
;
535 if (xml
->GetTag().Compare("Envelope", true))
536 goto bad_request_no_envelope
;
538 #if defined(PLATINUM_UPNP_SPECS_STRICT)
540 if (!xml
->GetNamespace() || xml
->GetNamespace()->Compare("http://schemas.xmlsoap.org/soap/envelope/"))
541 goto bad_request_upnp_not_strict
;
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
;
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
;
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
);
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();
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") {
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");
599 // verify all required arguments were passed
600 if (NPT_FAILED(action
->VerifyArguments(true))) {
601 action
->SetError(402, "Invalid or Missing Args");
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
)))) {
614 // create the soap response now
615 action
->FormatSoapResponse(*resp
);
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(),
629 action
->FormatSoapResponse(*resp
);
632 response
.SetStatus(500, "Internal Server Error");
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
,
641 entity
->SetContentType("text/xml; charset=\"utf-8\"");
642 response
.GetHeaders().SetHeader("Ext", ""); // should only be for M-POST but oh well
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
;
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
;
704 /*----------------------------------------------------------------------
705 | PLT_DeviceHost::ProcessHttpSubscriberRequest
706 +---------------------------------------------------------------------*/
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 ?
727 // make sure we don't have a callback nor a nt
728 if (nt
|| callback_urls
) {
733 NPT_Int32 timeout
= *PLT_Constants::GetInstance().GetDefaultSubscribeLease().AsPointer();
735 // subscription renewed
736 // send the info to the service
737 service
->ProcessRenewSubscription(context
.GetLocalAddress(),
743 // new subscription ?
744 // verify nt is present and valid
745 if (!nt
|| nt
->Compare("upnp:event", true)) {
746 response
.SetStatus(412, "Precondition failed");
749 // verify callback is present
750 if (!callback_urls
) {
751 response
.SetStatus(412, "Precondition failed");
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(),
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
) {
774 // subscription cancelled
775 // send the info to the service
776 service
->ProcessCancelSubscription(context
.GetLocalAddress(),
782 response
.SetStatus(412, "Precondition failed");
787 response
.SetStatus(400, "Bad Request");
791 /*----------------------------------------------------------------------
792 | PLT_DeviceHost::OnSsdpPacket
793 +---------------------------------------------------------------------*/
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,
822 NPT_CHECK_POINTER_SEVERE(st
);
824 if (url
.Compare("*") || protocol
.Compare("HTTP/1.1"))
827 const NPT_String
* man
= PLT_UPnPMessageHelper::GetMAN(request
);
828 if (!man
|| man
->Compare("\"ssdp:discover\"", true))
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
);
840 NPT_String prefix
= NPT_String::Format("Ignoring %s request from %s:%d",
842 (const char*) ip_address
, remote_port
);
843 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINE
, prefix
, &request
);
849 /*----------------------------------------------------------------------
850 | PLT_DeviceHost::SendSsdpSearchResponse
851 +---------------------------------------------------------------------*/
853 PLT_DeviceHost::SendSsdpSearchResponse(PLT_DeviceData
* device
,
854 NPT_HttpResponse
& response
,
855 NPT_UdpSocket
& socket
,
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
);
875 PLT_SsdpSender::SendSsdp(response
,
876 NPT_String("uuid:" + device
->m_UUID
+ "::upnp:rootdevice"),
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
);
891 PLT_SsdpSender::SendSsdp(response
,
892 "uuid:" + device
->m_UUID
,
893 "uuid:" + device
->m_UUID
,
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
,
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(),
932 for (int j
=0; j
< (int)device
->m_EmbeddedDevices
.GetItemCount(); j
++) {
933 SendSsdpSearchResponse(device
->m_EmbeddedDevices
[j
].AsPointer(),
943 /*----------------------------------------------------------------------
944 | PLT_DeviceHost::OnAction
945 +---------------------------------------------------------------------*/
947 PLT_DeviceHost::OnAction(PLT_ActionReference
& action
,
948 const PLT_HttpRequestContext
& context
)
950 NPT_COMPILER_UNUSED(context
);
951 action
->SetError(401, "Invalid Action");