Mercurial > projects > hoofbaby
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/deps/Platinum/Source/Core/PltDeviceHost.cpp Mon Jul 06 08:06:28 2009 -0700 @@ -0,0 +1,775 @@ +/***************************************************************** +| +| Platinum - Device Host +| +| Copyright (c) 2004-2008, Plutinosoft, LLC. +| All rights reserved. +| http://www.plutinosoft.com +| +| This program is free software; you can redistribute it and/or +| modify it under the terms of the GNU General Public License +| as published by the Free Software Foundation; either version 2 +| of the License, or (at your option) any later version. +| +| OEMs, ISVs, VARs and other distributors that combine and +| distribute commercially licensed software with Platinum software +| and do not wish to distribute the source code for the commercially +| licensed software under version 2, or (at your option) any later +| version, of the GNU General Public License (the "GPL") must enter +| into a commercial license agreement with Plutinosoft, LLC. +| +| This program is distributed in the hope that it will be useful, +| but WITHOUT ANY WARRANTY; without even the implied warranty of +| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +| GNU General Public License for more details. +| +| You should have received a copy of the GNU General Public License +| along with this program; see the file LICENSE.txt. If not, write to +| the Free Software Foundation, Inc., +| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +| http://www.gnu.org/licenses/gpl-2.0.html +| +****************************************************************/ + +/*---------------------------------------------------------------------- +| includes ++---------------------------------------------------------------------*/ +#include "PltService.h" +#include "PltDeviceHost.h" +#include "PltUPnP.h" +#include "PltXmlHelper.h" +#include "PltSsdp.h" +#include "PltHttpServer.h" +#include "PltVersion.h" + +NPT_SET_LOCAL_LOGGER("platinum.core.devicehost") + +/*---------------------------------------------------------------------- +| typedef ++---------------------------------------------------------------------*/ +typedef PLT_HttpRequestHandler<PLT_DeviceHost> PLT_HttpDeviceHostRequestHandler; + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::PLT_DeviceHost ++---------------------------------------------------------------------*/ +PLT_DeviceHost::PLT_DeviceHost(const char* description_path /* = "/" */, + const char* uuid /* = "" */, + const char* device_type /* = "" */, + const char* friendly_name /* = "" */, + bool show_ip /* = false */, + NPT_UInt16 port /* = 0 */, + bool port_rebind /* = false */) : + PLT_DeviceData(NPT_HttpUrl(NULL, 0, description_path), + uuid, + NPT_TimeInterval(1800, 0), + device_type, + friendly_name), + m_HttpServer(NULL), + m_Broadcast(false), + m_Port(port), + m_PortRebind(port_rebind) +{ + if (show_ip) { + NPT_List<NPT_String> ips; + PLT_UPnPMessageHelper::GetIPAddresses(ips); + if (ips.GetItemCount()) { + m_FriendlyName += " (" + *ips.GetFirstItem() + ")"; + } + } +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::~PLT_DeviceHost ++---------------------------------------------------------------------*/ +PLT_DeviceHost::~PLT_DeviceHost() +{ +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::AddIcon ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon, const char* filepath) +{ + NPT_HttpFileRequestHandler* icon_handler = + new NPT_HttpFileRequestHandler(icon.m_UrlPath, filepath); + m_HttpServer->AddRequestHandler(icon_handler, icon.m_UrlPath, false); + m_RequestHandlers.Add(icon_handler); + return m_Icons.Add(icon); +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::AddIcon ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon, + const void* data, + NPT_Size size, + bool copy /* = true */) +{ + NPT_HttpStaticRequestHandler* icon_handler = + new NPT_HttpStaticRequestHandler( + data, + size, + icon.m_MimeType, + copy); + m_HttpServer->AddRequestHandler(icon_handler, icon.m_UrlPath, false); + m_RequestHandlers.Add(icon_handler); + return m_Icons.Add(icon); +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::SetupIcons ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::SetupIcons() +{ + AddIcon( + PLT_DeviceIcon("image/jpeg", 120, 120, 24, "/images/platinum-120x120.jpg"), + "platinum-120x120.jpg"); + AddIcon( + PLT_DeviceIcon("image/jpeg", 48, 48, 24, "/images/platinum-48x48.jpg"), + "platinum-48x48.jpg"); + AddIcon( + PLT_DeviceIcon("image/png", 120, 120, 24, "/images/platinum-120x120.png"), + "platinum-120x120.png"); + AddIcon( + PLT_DeviceIcon("image/png", 48, 48, 24, "/images/platinum-48x48.png"), + "platinum-48x48.png"); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::SetupDevice ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::SetupDevice() +{ + NPT_CHECK_WARNING(SetupIcons()); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::SetupServiceSCPDHandler ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::SetupServiceSCPDHandler(PLT_Service* service) +{ + NPT_HttpUrl url; + NPT_String doc; + + // static scpd document + NPT_String scpd_url = service->GetSCPDURL(); + if (!scpd_url.StartsWith("/")) { + scpd_url = GetURLBase().GetPath() + scpd_url; + } + url.SetPathPlus(scpd_url); + NPT_CHECK_FATAL(service->GetSCPDXML(doc)); + + NPT_HttpStaticRequestHandler* scpd_handler = new NPT_HttpStaticRequestHandler(doc, "text/xml"); + m_HttpServer->AddRequestHandler(scpd_handler, url.GetPath(), false); + m_RequestHandlers.Add(scpd_handler); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::Start ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::Start(PLT_SsdpListenTask* task) +{ + // TODO: We should reuse address otherwise we might fail if we're passed the same port during fast restart +#ifdef _XBOX + m_HttpServer = new PLT_HttpServer(m_Port, m_PortRebind, 5); +#else + m_HttpServer = new PLT_HttpServer(m_Port, m_PortRebind, 20); // limit to 20 threads max +#endif + + NPT_CHECK_FATAL(SetupServices(*this)); + + // start the server + NPT_CHECK_SEVERE(m_HttpServer->Start()); + + // read back assigned port in case + // we passed 0 to randomly select one + m_Port = m_HttpServer->GetPort(); + m_URLDescription.SetPort(m_Port); + + // callback to initialize the device + NPT_CHECK_FATAL(SetupDevice()); + + // set up static handlers first as the order is important + + // services static root device scpd documents + for (NPT_Cardinal i=0; i<m_Services.GetItemCount(); i++) { + SetupServiceSCPDHandler(m_Services[i]); + } + + // services static embedded devices scpd documents + for (NPT_Cardinal j=0; j<m_EmbeddedDevices.GetItemCount(); j++) { + for (NPT_Cardinal i=0; i<m_EmbeddedDevices[j]->m_Services.GetItemCount(); i++) { + SetupServiceSCPDHandler(m_EmbeddedDevices[j]->m_Services[i]); + } + } + + // all other requests including description document + // and service control are dynamically handled + PLT_HttpDeviceHostRequestHandler* device_handler = new PLT_HttpDeviceHostRequestHandler(this); + m_RequestHandlers.Add(device_handler); + m_HttpServer->AddRequestHandler(device_handler, "/", true); + + // we should not advertise right away + // spec says randomly less than 100ms + NPT_TimeInterval delay(0, NPT_System::GetRandomInteger() % 100000000); + + // calculate when we should send another announcement + NPT_Size leaseTime = (NPT_Size)(float)GetLeaseTime(); + NPT_TimeInterval repeat; + repeat.m_Seconds = leaseTime?(int)((leaseTime >> 1) + ((unsigned short)NPT_System::GetRandomInteger() % (leaseTime >> 2))):30; + + // the XBOX cannot receive multicast, so we blast every 7 secs +#ifdef _XBOX + repeat.m_Seconds = 7; +#endif + + PLT_ThreadTask* announce_task = new PLT_SsdpDeviceAnnounceTask( + this, + repeat, + true, + m_Broadcast); + m_TaskManager.StartTask(announce_task, &delay); + + // register ourselves as a listener for ssdp requests + task->AddListener(this); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::Stop ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::Stop(PLT_SsdpListenTask* task) +{ + // unregister ourselves as a listener for ssdp requests + task->RemoveListener(this); + + // remove all our running tasks + m_TaskManager.StopAllTasks(); + + if (m_HttpServer) { + // stop our internal http server + m_HttpServer->Stop(); + delete m_HttpServer; + m_HttpServer = NULL; + + // notify we're gone + NPT_List<NPT_NetworkInterface*> if_list; + PLT_UPnPMessageHelper::GetNetworkInterfaces(if_list); + if_list.Apply(PLT_SsdpAnnounceInterfaceIterator(this, true, m_Broadcast)); + if_list.Apply(NPT_ObjectDeleter<NPT_NetworkInterface>()); + } + + m_RequestHandlers.Apply(NPT_ObjectDeleter<NPT_HttpRequestHandler>()); + m_RequestHandlers.Clear(); + + PLT_DeviceData::Cleanup(); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::Announce ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::Announce(PLT_DeviceData* device, + NPT_HttpRequest& req, + NPT_UdpSocket& socket, + bool byebye) +{ + NPT_Result res = NPT_SUCCESS; + + NPT_LOG_INFO_3("Sending SSDP NOTIFY (%s) Request to %s with location:%s", + byebye?"ssdp:byebye":"ssdp:alive", + (const char*)req.GetUrl().ToString(), + (const char*)(PLT_UPnPMessageHelper::GetLocation(req)?*PLT_UPnPMessageHelper::GetLocation(req):"")); + + if (byebye == false) { + // get location URL based on ip address of interface + PLT_UPnPMessageHelper::SetNTS(req, "ssdp:alive"); + PLT_UPnPMessageHelper::SetLeaseTime(req, (NPT_Timeout)(float)device->GetLeaseTime()); + PLT_UPnPMessageHelper::SetServer(req, NPT_HttpServer::m_ServerHeader, false); + } else { + PLT_UPnPMessageHelper::SetNTS(req, "ssdp:byebye"); + } + + // target address + NPT_IpAddress ip; + if (NPT_FAILED(res = ip.ResolveName(req.GetUrl().GetHost()))) { + return res; + } + NPT_SocketAddress addr(ip, req.GetUrl().GetPort()); + + // upnp:rootdevice + if (device->m_ParentUUID.IsEmpty()) { + PLT_SsdpSender::SendSsdp(req, + NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"), + "upnp:rootdevice", + socket, + true, + &addr); + } + + // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver + PLT_SsdpSender::SendSsdp(req, + NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType), + device->m_DeviceType, + socket, + true, + &addr); + + // services + for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) { + // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver + PLT_SsdpSender::SendSsdp(req, + NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()), + device->m_Services[i]->GetServiceType(), + socket, + true, + &addr); + } + + // uuid:device-UUID + PLT_SsdpSender::SendSsdp(req, + "uuid:" + device->m_UUID, + "uuid:" + device->m_UUID, + socket, + true, + &addr); + + + // embedded devices + for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) { + Announce(device->m_EmbeddedDevices[j].AsPointer(), + req, + socket, + byebye); + } + + return res; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::ProcessHttpRequest ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::ProcessHttpRequest(NPT_HttpRequest& request, + const NPT_HttpRequestContext& context, + NPT_HttpResponse& response) +{ + // get the address of who sent us some data back*/ + NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString(); + NPT_String method = request.GetMethod(); + NPT_String protocol = request.GetProtocol(); + + if (method.Compare("POST") == 0) { + return ProcessHttpPostRequest(request, context, response); + } else if (method.Compare("SUBSCRIBE") == 0 || method.Compare("UNSUBSCRIBE") == 0) { + return ProcessHttpSubscriberRequest(request, context, response); + } else if (method.Compare("GET") == 0) { + if (request.GetUrl().GetPath() == m_URLDescription.GetPath()) { + return ProcessGetDescription(request, context, response); + } + } + + response.SetStatus(405, "Bad Request"); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::ProcessGetDescription ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::ProcessGetDescription(NPT_HttpRequest& /*request*/, + const NPT_HttpRequestContext& /*context*/, + NPT_HttpResponse& response) +{ + NPT_String doc; + NPT_CHECK_FATAL(GetDescription(doc)); + PLT_HttpHelper::SetBody(response, doc); + PLT_HttpHelper::SetContentType(response, "text/xml"); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::ProcessPostRequest ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::ProcessHttpPostRequest(NPT_HttpRequest& request, + const NPT_HttpRequestContext& context, + NPT_HttpResponse& response) +{ + NPT_Result res; + NPT_String service_type; + NPT_String str; + NPT_XmlElementNode* xml = NULL; + NPT_String soap_action_header; + PLT_Service* service; + NPT_XmlElementNode* soap_body; + NPT_XmlElementNode* soap_action; + const NPT_String* attr; + PLT_ActionDesc* action_desc; + PLT_ActionReference action; + NPT_MemoryStreamReference resp(new NPT_MemoryStream); + NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString(); + NPT_String method = request.GetMethod(); + NPT_String url = request.GetUrl().ToRequestString(true); + NPT_String protocol = request.GetProtocol(); + + if (NPT_FAILED(FindServiceByControlURI(url, service))) + goto bad_request; + + if (!request.GetHeaders().GetHeaderValue("SOAPAction")) + goto bad_request; + + // extract the soap action name from the header + soap_action_header = *request.GetHeaders().GetHeaderValue("SOAPAction"); + soap_action_header.TrimLeft('"'); + soap_action_header.TrimRight('"'); + char prefix[200]; + char soap_action_name[100]; + int ret; + //FIXME: no sscanf + ret = sscanf(soap_action_header, "%[^#]#%s", + prefix, + soap_action_name); + if (ret != 2) + goto bad_request; + + // read the xml body and parse it + if (NPT_FAILED(PLT_HttpHelper::ParseBody(request, xml))) + goto bad_request; + + // check envelope + if (xml->GetTag().Compare("Envelope", true)) + goto bad_request; + + // check namespace + if (!xml->GetNamespace() || xml->GetNamespace()->Compare("http://schemas.xmlsoap.org/soap/envelope/")) + goto bad_request; + + // check encoding + attr = xml->GetAttribute("encodingStyle", "http://schemas.xmlsoap.org/soap/envelope/"); + if (!attr || attr->Compare("http://schemas.xmlsoap.org/soap/encoding/")) + goto bad_request; + + // read action + soap_body = PLT_XmlHelper::GetChild(xml, "Body"); + if (soap_body == NULL) + goto bad_request; + + PLT_XmlHelper::GetChild(soap_body, soap_action); + if (soap_action == NULL) + goto bad_request; + + // verify action name is identical to SOAPACTION header*/ + if (soap_action->GetTag().Compare(soap_action_name, true)) + goto bad_request; + + // verify namespace + if (!soap_action->GetNamespace() || soap_action->GetNamespace()->Compare(service->GetServiceType())) + goto bad_request; + + // create a buffer for our response body and call the service + if ((action_desc = service->FindActionDesc(soap_action_name)) == NULL) { + res = NPT_FAILURE; + // create a bastard soap response + PLT_Action::FormatSoapError(401, "Invalid Action", *resp); + goto error; + } + + // create an action object + action = new PLT_Action(action_desc); + + // read all the arguments if any + for (NPT_List<NPT_XmlNode*>::Iterator args = soap_action->GetChildren().GetFirstItem(); args; args++) { + NPT_XmlElementNode* child = (*args)->AsElementNode(); + if (!child) continue; + + // Total HACK for xbox360 upnp uncompliance! + NPT_String name = child->GetTag(); + if (action_desc->GetName() == "Browse" && name == "ContainerID") { + name = "ObjectID"; + } + + res = action->SetArgumentValue( + name, + child->GetText()?*child->GetText():""); + + if (NPT_FAILED(res)) { + // FIXME: incorrect upnp error? + PLT_Action::FormatSoapError(401, "Invalid Action", *resp); + goto error; + } + } + + if (NPT_FAILED(action->VerifyArguments(true))) { + action->SetError(402, "Invalid Args"); + goto error; + } + + // call the virtual function, it's all good + // set the error in case the it wasn't done + if (NPT_FAILED(OnAction(action, PLT_HttpRequestContext(request, context)))) { + goto error; + } + + // create the soap response now + action->FormatSoapResponse(*resp); + goto done; + +error: + if (!action.IsNull() && action->GetErrorCode() == 0) { + action->SetError(501, "Action Failed"); + action->FormatSoapResponse(*resp); + } + response.SetStatus(500, "Internal Server Error"); + +done: + //args.Apply(NPT_ObjectDeleter<PLT_Argument>()); + + NPT_LargeSize resp_body_size; + if (NPT_SUCCEEDED(resp->GetAvailable(resp_body_size))) { + PLT_HttpHelper::SetContentType(response, "text/xml; charset=\"utf-8\""); + response.GetHeaders().SetHeader("Ext", ""); // should only be for M-POST but oh well + NPT_InputStreamReference input = resp; + PLT_HttpHelper::SetBody(response, input); + } + + delete xml; + return NPT_SUCCESS; + +bad_request: + delete xml; + response.SetStatus(400, "Bad Request"); + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::ProcessHttpSubscriberRequest ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::ProcessHttpSubscriberRequest(NPT_HttpRequest& request, + const NPT_HttpRequestContext& context, + NPT_HttpResponse& response) +{ + NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString(); + NPT_String method = request.GetMethod(); + NPT_String url = request.GetUrl().ToRequestString(true); + NPT_String protocol = request.GetProtocol(); + + const NPT_String* nt = PLT_UPnPMessageHelper::GetNT(request); + const NPT_String* callback_urls = PLT_UPnPMessageHelper::GetCallbacks(request); + const NPT_String* sid = PLT_UPnPMessageHelper::GetSID(request); + + PLT_Service* service; + if (NPT_FAILED(FindServiceByEventSubURI(url, service))) { + goto cleanup; + } + + if (method.Compare("SUBSCRIBE") == 0) { + // Do we have a sid ? + if (sid) { + // make sure we don't have a callback nor a nt + if (nt || callback_urls) { + goto cleanup; + } + + NPT_Int32 timeout; + if (NPT_FAILED(PLT_UPnPMessageHelper::GetTimeOut(request, timeout))) { + timeout = 1800; + } + // subscription renewed + // send the info to the service + service->ProcessRenewSubscription(context.GetLocalAddress(), + *sid, + timeout, + response); + return NPT_SUCCESS; + } else { + // new subscription ? + // verify nt is present and valid + if (!nt || nt->Compare("upnp:event", true)) { + response.SetStatus(412, "Precondition failed"); + return NPT_FAILURE; + } + // verify callback is present + if (!callback_urls) { + response.SetStatus(412, "Precondition failed"); + return NPT_FAILURE; + } + + NPT_Int32 timeout; + if (NPT_FAILED(PLT_UPnPMessageHelper::GetTimeOut(request, timeout))) { + timeout = 1800; + } + + // send the info to the service + service->ProcessNewSubscription(&m_TaskManager, + context.GetLocalAddress(), + *callback_urls, + timeout, + response); + return NPT_SUCCESS; + } + } else if (method.Compare("UNSUBSCRIBE") == 0) { + // Do we have a sid ? + if (sid && sid->GetLength() > 0) { + // make sure we don't have a callback nor a nt + if (nt || callback_urls) { + goto cleanup; + } + + // subscription cancelled + // send the info to the service + service->ProcessCancelSubscription(context.GetLocalAddress(), + *sid, + response); + return NPT_SUCCESS; + } + } + +cleanup: + response.SetStatus(405, "Bad Request"); + return NPT_FAILURE; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::OnSsdpPacket ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::OnSsdpPacket(NPT_HttpRequest& request, + const NPT_HttpRequestContext& context) +{ + return ProcessSsdpSearchRequest(request, context); +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::ProcessSsdpSearchRequest ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::ProcessSsdpSearchRequest(NPT_HttpRequest& request, + const NPT_HttpRequestContext& context) +{ + // get the address of who sent us some data back*/ + NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString(); + NPT_String method = request.GetMethod(); + NPT_String url = request.GetUrl().ToRequestString(true); + NPT_String protocol = request.GetProtocol(); + + if (method.Compare("M-SEARCH") == 0) { + NPT_LOG_INFO_1("Received M-SEARCH from %s", (const char*) ip_address); + PLT_LOG_HTTP_MESSAGE(NPT_LOG_LEVEL_FINE, &request); + + if (url.Compare("*") || protocol.Compare("HTTP/1.1")) + return NPT_FAILURE; + + const NPT_String* st = PLT_UPnPMessageHelper::GetST(request); + NPT_CHECK_POINTER_SEVERE(st); + + const NPT_String* man = PLT_UPnPMessageHelper::GetMAN(request); + if (!man || man->Compare("\"ssdp:discover\"", true)) + return NPT_FAILURE; + + NPT_UInt32 mx; + if (NPT_FAILED(PLT_UPnPMessageHelper::GetMX(request, mx))) + return NPT_FAILURE; + + // create a task to respond to the request + NPT_TimeInterval timer((mx==0)?0:((int)(0 + ((unsigned short)NPT_System::GetRandomInteger() % ((mx>10)?10:mx)))), 0); + PLT_SsdpDeviceSearchResponseTask* task = new PLT_SsdpDeviceSearchResponseTask(this, context.GetRemoteAddress(), *st); + m_TaskManager.StartTask(task, &timer); + return NPT_SUCCESS; + } + + return NPT_FAILURE; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::SendSsdpSearchResponse ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::SendSsdpSearchResponse(PLT_DeviceData* device, + NPT_HttpResponse& response, + NPT_UdpSocket& socket, + const char* st, + const NPT_SocketAddress* addr /* = NULL */) +{ + NPT_LOG_INFO_1("Responding to a M-SEARCH request for %s", st); + + // ssdp:all or upnp:rootdevice + if (NPT_String::Compare(st, "ssdp:all") == 0 || NPT_String::Compare(st, "upnp:rootdevice") == 0) { + if (device->m_ParentUUID.IsEmpty()) { + // upnp:rootdevice + PLT_SsdpSender::SendSsdp(response, + NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"), + "upnp:rootdevice", + socket, + false, + addr); + } + } + + // uuid:device-UUID + if (NPT_String::Compare(st, "ssdp:all", false) == 0 || NPT_String::Compare(st, "uuid:" + device->m_UUID, false) == 0) { + // uuid:device-UUID + PLT_SsdpSender::SendSsdp(response, + "uuid:" + device->m_UUID, + "uuid:" + device->m_UUID, + socket, + false, + addr); + } + + // urn:schemas-upnp-org:device:deviceType:ver + if (NPT_String::Compare(st, "ssdp:all", false) == 0 || NPT_String::Compare(st, device->m_DeviceType, false) == 0) { + // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver + PLT_SsdpSender::SendSsdp(response, + NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType), + device->m_DeviceType, + socket, + false, + addr); + } + + // services + for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) { + if (NPT_String::Compare(st, "ssdp:all", false) == 0 || NPT_String::Compare(st, device->m_Services[i]->GetServiceType(), false) == 0) { + // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver + PLT_SsdpSender::SendSsdp(response, + NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()), + device->m_Services[i]->GetServiceType(), + socket, + false, + addr); + } + } + + // embedded devices + for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) { + SendSsdpSearchResponse(device->m_EmbeddedDevices[j].AsPointer(), response, socket, st, addr); + } + + return NPT_SUCCESS; +} + +/*---------------------------------------------------------------------- +| PLT_DeviceHost::OnAction ++---------------------------------------------------------------------*/ +NPT_Result +PLT_DeviceHost::OnAction(PLT_ActionReference& action, + const PLT_HttpRequestContext& context) +{ + NPT_COMPILER_UNUSED(context); + action->SetError(401, "Invalid Action"); + return NPT_FAILURE; +} +