1 /*****************************************************************
2 |
3 | Platinum - Device Host
4 |
5 | Copyright (c) 2004-2010, Plutinosoft, LLC.
6 | All rights reserved.
7 | http://www.plutinosoft.com
8 |
9 | This program is free software; you can redistribute it and/or
10 | modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.
13 |
14 | OEMs, ISVs, VARs and other distributors that combine and
15 | distribute commercially licensed software with Platinum software
16 | and do not wish to distribute the source code for the commercially
17 | licensed software under version 2, or (at your option) any later
18 | version, of the GNU General Public License (the "GPL") must enter
19 | into a commercial license agreement with Plutinosoft, LLC.
20 | licensing@plutinosoft.com
21 |
22 | This program is distributed in the hope that it will be useful,
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 | GNU General Public License for more details.
26 |
27 | You should have received a copy of the GNU General Public License
28 | along with this program; see the file LICENSE.txt. If not, write to
29 | the Free Software Foundation, Inc.,
30 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 | http://www.gnu.org/licenses/gpl-2.0.html
32 |
33 ****************************************************************/
34
35 /*----------------------------------------------------------------------
36 | includes
37 +---------------------------------------------------------------------*/
38 #include "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"
45
46 NPT_SET_LOCAL_LOGGER("platinum.core.devicehost")
47
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];
55
56 /*----------------------------------------------------------------------
57 | PLT_DeviceHost::PLT_DeviceHost
58 +---------------------------------------------------------------------*/
PLT_DeviceHost(const char * description_path,const char * uuid,const char * device_type,const char * friendly_name,bool show_ip,NPT_UInt16 port,bool port_rebind)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)
78 {
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() + ")";
84 }
85 }
86 }
87
88 /*----------------------------------------------------------------------
89 | PLT_DeviceHost::~PLT_DeviceHost
90 +---------------------------------------------------------------------*/
~PLT_DeviceHost()91 PLT_DeviceHost::~PLT_DeviceHost()
92 {
93 }
94
95 /*----------------------------------------------------------------------
96 | PLT_DeviceHost::AddIcon
97 +---------------------------------------------------------------------*/
98 NPT_Result
AddIcon(const PLT_DeviceIcon & icon,const char * fileroot,const char * urlroot)99 PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon,
100 const char* fileroot,
101 const char* urlroot /* = "/" */)
102 {
103 // verify the url of the icon starts with the url root
104 if (!icon.m_UrlPath.StartsWith(urlroot)) return NPT_ERROR_INVALID_PARAMETERS;
105
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);
109 }
110
111 /*----------------------------------------------------------------------
112 | PLT_DeviceHost::AddIcon
113 +---------------------------------------------------------------------*/
114 NPT_Result
AddIcon(const PLT_DeviceIcon & icon,const void * data,NPT_Size size,bool copy)115 PLT_DeviceHost::AddIcon(const PLT_DeviceIcon& icon,
116 const void* data,
117 NPT_Size size,
118 bool copy /* = true */)
119 {
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);
128 }
129
130 /*----------------------------------------------------------------------
131 | PLT_DeviceHost::SetupIcons
132 +---------------------------------------------------------------------*/
133 NPT_Result
SetupIcons()134 PLT_DeviceHost::SetupIcons()
135 {
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);
149 }*/
150 return NPT_SUCCESS;
151 }
152
153 /*----------------------------------------------------------------------
154 | PLT_DeviceHost::SetupDevice
155 +---------------------------------------------------------------------*/
156 NPT_Result
SetupDevice()157 PLT_DeviceHost::SetupDevice()
158 {
159 NPT_CHECK_FATAL(SetupServices());
160 NPT_CHECK_WARNING(SetupIcons());
161 return NPT_SUCCESS;
162 }
163
164 /*----------------------------------------------------------------------
165 | PLT_DeviceHost::Start
166 +---------------------------------------------------------------------*/
167 NPT_Result
Start(PLT_SsdpListenTask * task)168 PLT_DeviceHost::Start(PLT_SsdpListenTask* task)
169 {
170 NPT_Result result;
171
172 if (m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
173
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);
181 }
182
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);
186
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);
192 }
193
194 // all other requests including description document
195 // and service control are dynamically handled
196 m_HttpServer->AddRequestHandler(new PLT_HttpRequestHandler(this), "/", true, true);
197
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);
201
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);
208
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);
215
216 // register ourselves as a listener for SSDP search requests
217 task->AddListener(this);
218
219 m_Started = true;
220 return NPT_SUCCESS;
221 }
222
223 /*----------------------------------------------------------------------
224 | PLT_DeviceHost::Stop
225 +---------------------------------------------------------------------*/
226 NPT_Result
Stop(PLT_SsdpListenTask * task)227 PLT_DeviceHost::Stop(PLT_SsdpListenTask* task)
228 {
229 if (!m_Started) NPT_CHECK_WARNING(NPT_ERROR_INVALID_STATE);
230
231 // mark immediately we're stopping
232 m_Started = false;
233
234 // unregister ourselves as a listener for ssdp requests
235 task->RemoveListener(this);
236
237 // remove all our running tasks
238 m_TaskManager->Abort();
239
240 // stop our internal http server
241 m_HttpServer->Stop();
242
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>());
248
249 // Cleanup all services and embedded devices
250 PLT_DeviceData::Cleanup();
251
252 m_HttpServer = NULL;
253 m_TaskManager = NULL;
254
255 return NPT_SUCCESS;
256 }
257
258 /*----------------------------------------------------------------------
259 | PLT_DeviceHost::Announce
260 +---------------------------------------------------------------------*/
261 NPT_Result
Announce(PLT_DeviceData * device,NPT_HttpRequest & req,NPT_UdpSocket & socket,PLT_SsdpAnnounceType type)262 PLT_DeviceHost::Announce(PLT_DeviceData* device,
263 NPT_HttpRequest& req,
264 NPT_UdpSocket& socket,
265 PLT_SsdpAnnounceType type)
266 {
267 NPT_Result res = NPT_SUCCESS;
268
269 // target address
270 NPT_IpAddress ip;
271 NPT_CHECK_FATAL(ip.ResolveName(req.GetUrl().GetHost()));
272 NPT_SocketAddress addr(ip, req.GetUrl().GetPort());
273
274 // // UPnP 1.1 BOOTID.UPNP.ORG header
275 // PLT_UPnPMessageHelper::SetBootId(req, device->m_BootId);
276 //
277 // // UPnP 1.1 CONFIGID.UPNP.ORG header
278 // if (device->m_ConfigId > 0) {
279 // PLT_UPnPMessageHelper::SetConfigId(req, device->m_ConfigId);
280 // }
281
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;
290
291 case PLT_ANNOUNCETYPE_BYEBYE:
292 nts = "ssdp:byebye";
293 break;
294
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);
300 }
301 PLT_UPnPMessageHelper::SetNextBootId(req, device->m_NextBootId);
302 break;
303
304 default:
305 break;
306 }
307 PLT_UPnPMessageHelper::SetNTS(req, nts);
308
309 NPT_LOG_FINER_3("Sending SSDP NOTIFY (%s) Request to %s (%s)",
310 nts.GetChars(),
311 (const char*)req.GetUrl().ToString(),
312 (const char*)(PLT_UPnPMessageHelper::GetLocation(req)?*PLT_UPnPMessageHelper::GetLocation(req):""));
313
314 // upnp:rootdevice
315 if (device->m_ParentUUID.IsEmpty()) {
316 PLT_SsdpSender::SendSsdp(req,
317 NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"),
318 "upnp:rootdevice",
319 socket,
320 true,
321 &addr);
322 }
323
324 // on byebye, don't sleep otherwise it hangs when we stop upnp
325 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
326 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
327 }
328
329 // uuid:device-UUID
330 PLT_SsdpSender::SendSsdp(req,
331 "uuid:" + device->m_UUID,
332 "uuid:" + device->m_UUID,
333 socket,
334 true,
335 &addr);
336
337 // on byebye, don't sleep otherwise it hangs when we stop upnp
338 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
339 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
340 }
341
342 // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver
343 PLT_SsdpSender::SendSsdp(req,
344 NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType),
345 device->m_DeviceType,
346 socket,
347 true,
348 &addr);
349
350 // on byebye, don't sleep otherwise it hangs when we stop upnp
351 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
352 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
353 }
354
355 // services
356 for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) {
357 // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver
358 PLT_SsdpSender::SendSsdp(req,
359 NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()),
360 device->m_Services[i]->GetServiceType(),
361 socket,
362 true,
363 &addr);
364
365 // on byebye, don't sleep otherwise it hangs when we stop upnp
366 if (type != PLT_ANNOUNCETYPE_BYEBYE) {
367 NPT_System::Sleep(NPT_TimeInterval(PLT_DLNA_SSDP_DELAY));
368 }
369 }
370
371 // embedded devices
372 for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) {
373 Announce(device->m_EmbeddedDevices[j].AsPointer(),
374 req,
375 socket,
376 type);
377 }
378
379 return res;
380 }
381
382 /*----------------------------------------------------------------------
383 | PLT_DeviceHost::SetupResponse
384 +---------------------------------------------------------------------*/
385 NPT_Result
SetupResponse(NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)386 PLT_DeviceHost::SetupResponse(NPT_HttpRequest& request,
387 const NPT_HttpRequestContext& context,
388 NPT_HttpResponse& response)
389 {
390 // get the address of who sent us some data back*/
391 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
392 NPT_String method = request.GetMethod();
393 NPT_String protocol = request.GetProtocol();
394
395 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINER, "PLT_DeviceHost::SetupResponse:", &request);
396
397 if (method.Compare("POST") == 0) {
398 return ProcessHttpPostRequest(request, context, response);
399 } else if (method.Compare("SUBSCRIBE") == 0 || method.Compare("UNSUBSCRIBE") == 0) {
400 return ProcessHttpSubscriberRequest(request, context, response);
401 } else if (method.Compare("GET") == 0 || method.Compare("HEAD") == 0) {
402 // process SCPD requests
403 PLT_Service* service;
404 if (NPT_SUCCEEDED(FindServiceBySCPDURL(request.GetUrl().ToRequestString(), service, true))) {
405 return ProcessGetSCPD(service, request, context, response);
406 }
407
408 // process Description document requests
409 if (request.GetUrl().GetPath() == m_URLDescription.GetPath()) {
410 return ProcessGetDescription(request, context, response);
411 }
412
413 // process other requests
414 return ProcessHttpGetRequest(request, context, response);
415 }
416
417 response.SetStatus(405, "Bad Request");
418 return NPT_SUCCESS;
419 }
420
421 /*----------------------------------------------------------------------
422 | PLT_DeviceHost::ProcessHttpGetRequest
423 +---------------------------------------------------------------------*/
424 NPT_Result
ProcessHttpGetRequest(NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)425 PLT_DeviceHost::ProcessHttpGetRequest(NPT_HttpRequest& request,
426 const NPT_HttpRequestContext& context,
427 NPT_HttpResponse& response)
428 {
429 NPT_COMPILER_UNUSED(request);
430 NPT_COMPILER_UNUSED(context);
431 NPT_COMPILER_UNUSED(response);
432
433 return NPT_ERROR_NO_SUCH_ITEM;
434 }
435
436 /*----------------------------------------------------------------------
437 | PLT_DeviceHost::ProcessGetDescription
438 +---------------------------------------------------------------------*/
439 NPT_Result
ProcessGetDescription(NPT_HttpRequest &,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)440 PLT_DeviceHost::ProcessGetDescription(NPT_HttpRequest& /*request*/,
441 const NPT_HttpRequestContext& context,
442 NPT_HttpResponse& response)
443 {
444 NPT_COMPILER_UNUSED(context);
445
446 NPT_String doc;
447 NPT_CHECK_FATAL(GetDescription(doc));
448 NPT_LOG_FINEST_2("Returning description to %s: %s",
449 (const char*)context.GetRemoteAddress().GetIpAddress().ToString(),
450 (const char*)doc);
451
452 NPT_HttpEntity* entity;
453 PLT_HttpHelper::SetBody(response, doc, &entity);
454 entity->SetContentType("text/xml; charset=\"utf-8\"");
455 return NPT_SUCCESS;
456 }
457
458 /*----------------------------------------------------------------------
459 | PLT_DeviceHost::ProcessGetSCPD
460 +---------------------------------------------------------------------*/
461 NPT_Result
ProcessGetSCPD(PLT_Service * service,NPT_HttpRequest &,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)462 PLT_DeviceHost::ProcessGetSCPD(PLT_Service* service,
463 NPT_HttpRequest& /*request*/,
464 const NPT_HttpRequestContext& context,
465 NPT_HttpResponse& response)
466 {
467 NPT_COMPILER_UNUSED(context);
468 NPT_CHECK_POINTER_FATAL(service);
469
470 NPT_String doc;
471 NPT_CHECK_FATAL(service->GetSCPDXML(doc));
472 NPT_LOG_FINEST_2("Returning SCPD to %s: %s",
473 (const char*)context.GetRemoteAddress().GetIpAddress().ToString(),
474 (const char*)doc);
475
476 NPT_HttpEntity* entity;
477 PLT_HttpHelper::SetBody(response, doc, &entity);
478 entity->SetContentType("text/xml; charset=\"utf-8\"");
479 return NPT_SUCCESS;
480 }
481
482 /*----------------------------------------------------------------------
483 | PLT_DeviceHost::ProcessPostRequest
484 +---------------------------------------------------------------------*/
485 NPT_Result
ProcessHttpPostRequest(NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)486 PLT_DeviceHost::ProcessHttpPostRequest(NPT_HttpRequest& request,
487 const NPT_HttpRequestContext& context,
488 NPT_HttpResponse& response)
489 {
490 NPT_Result res;
491 NPT_String service_type;
492 NPT_String str;
493 NPT_XmlElementNode* xml = NULL;
494 NPT_String soap_action_header;
495 PLT_Service* service;
496 NPT_XmlElementNode* soap_body;
497 NPT_XmlElementNode* soap_action;
498 PLT_ActionDesc* action_desc;
499 PLT_ActionReference action;
500 NPT_MemoryStreamReference resp(new NPT_MemoryStream);
501 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
502 NPT_String method = request.GetMethod();
503 NPT_String url = request.GetUrl().ToRequestString();
504 NPT_String protocol = request.GetProtocol();
505 NPT_List<NPT_String> components;
506 NPT_String soap_action_name;
507
508 #if defined(PLATINUM_UPNP_SPECS_STRICT)
509 const NPT_String* attr;
510 #endif
511
512 if (NPT_FAILED(FindServiceByControlURL(url, service, true)))
513 goto bad_request;
514
515 if (!request.GetHeaders().GetHeaderValue("SOAPAction"))
516 goto bad_request;
517
518 // extract the soap action name from the header
519 soap_action_header = *request.GetHeaders().GetHeaderValue("SOAPAction");
520 soap_action_header.TrimLeft('"');
521 soap_action_header.TrimRight('"');
522
523 components = soap_action_header.Split("#");
524 if (components.GetItemCount() != 2)
525 goto bad_request;
526
527 soap_action_name = *components.GetItem(1);
528
529 // read the xml body and parse it
530 if (NPT_FAILED(PLT_HttpHelper::ParseBody(request, xml)))
531 goto bad_request;
532
533 // check envelope
534 if (xml->GetTag().Compare("Envelope", true))
535 goto bad_request;
536
537 #if defined(PLATINUM_UPNP_SPECS_STRICT)
538 // check namespace
539 if (!xml->GetNamespace() || xml->GetNamespace()->Compare("http://schemas.xmlsoap.org/soap/envelope/"))
540 goto bad_request;
541
542 // check encoding
543 attr = xml->GetAttribute("encodingStyle", "http://schemas.xmlsoap.org/soap/envelope/");
544 if (!attr || attr->Compare("http://schemas.xmlsoap.org/soap/encoding/"))
545 goto bad_request;
546 #endif
547
548 // read action
549 soap_body = PLT_XmlHelper::GetChild(xml, "Body");
550 if (soap_body == NULL)
551 goto bad_request;
552
553 PLT_XmlHelper::GetChild(soap_body, soap_action);
554 if (soap_action == NULL)
555 goto bad_request;
556
557 // verify action name is identical to SOAPACTION header*/
558 if (soap_action->GetTag().Compare(soap_action_name, true))
559 goto bad_request;
560
561 // verify namespace
562 if (!soap_action->GetNamespace() || soap_action->GetNamespace()->Compare(service->GetServiceType()))
563 goto bad_request;
564
565 // create a buffer for our response body and call the service
566 if ((action_desc = service->FindActionDesc(soap_action_name)) == NULL) {
567 // create a bastard soap response
568 PLT_Action::FormatSoapError(401, "Invalid Action", *resp);
569 goto error;
570 }
571
572 // create a new action object
573 action = new PLT_Action(*action_desc);
574
575 // read all the arguments if any
576 for (NPT_List<NPT_XmlNode*>::Iterator args = soap_action->GetChildren().GetFirstItem();
577 args;
578 args++) {
579 NPT_XmlElementNode* child = (*args)->AsElementNode();
580 if (!child) continue;
581
582 // Total HACK for xbox360 upnp uncompliance!
583 NPT_String name = child->GetTag();
584 if (action_desc->GetName() == "Browse" && name == "ContainerID") {
585 name = "ObjectID";
586 }
587
588 res = action->SetArgumentValue(
589 name,
590 child->GetText()?*child->GetText():"");
591
592 // test if value was correct
593 if (res == NPT_ERROR_INVALID_PARAMETERS) {
594 action->SetError(701, "Invalid Name");
595 goto error;
596 }
597 }
598
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;
603 }
604
605 NPT_LOG_FINE_2("Processing action \"%s\" from %s",
606 (const char*)action->GetActionDesc().GetName(),
607 (const char*)context.GetRemoteAddress().GetIpAddress().ToString());
608
609 // call the virtual function, it's all good
610 if (NPT_FAILED(OnAction(action, PLT_HttpRequestContext(request, context)))) {
611 goto error;
612 }
613
614 // create the soap response now
615 action->FormatSoapResponse(*resp);
616 goto done;
617
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");
623 }
624 NPT_LOG_WARNING_3("Error while processing action %s: %d %s",
625 (const char*)action->GetActionDesc().GetName(),
626 action->GetErrorCode(),
627 action->GetError());
628
629 action->FormatSoapResponse(*resp);
630 }
631
632 response.SetStatus(500, "Internal Server Error");
633
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
643 }
644
645 delete xml;
646 return NPT_SUCCESS;
647
648 bad_request:
649 delete xml;
650 response.SetStatus(500, "Bad Request");
651 return NPT_SUCCESS;
652 }
653
654 /*----------------------------------------------------------------------
655 | PLT_DeviceHost::ProcessHttpSubscriberRequest
656 +---------------------------------------------------------------------*/
657 NPT_Result
ProcessHttpSubscriberRequest(NPT_HttpRequest & request,const NPT_HttpRequestContext & context,NPT_HttpResponse & response)658 PLT_DeviceHost::ProcessHttpSubscriberRequest(NPT_HttpRequest& request,
659 const NPT_HttpRequestContext& context,
660 NPT_HttpResponse& response)
661 {
662 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
663 NPT_String method = request.GetMethod();
664 NPT_String url = request.GetUrl().ToRequestString();
665 NPT_String protocol = request.GetProtocol();
666
667 const NPT_String* nt = PLT_UPnPMessageHelper::GetNT(request);
668 const NPT_String* callback_urls = PLT_UPnPMessageHelper::GetCallbacks(request);
669 const NPT_String* sid = PLT_UPnPMessageHelper::GetSID(request);
670
671 PLT_Service* service;
672 NPT_CHECK_LABEL_WARNING(FindServiceByEventSubURL(url, service, true), cleanup);
673
674 if (method.Compare("SUBSCRIBE") == 0) {
675 // Do we have a sid ?
676 if (sid) {
677 // make sure we don't have a callback nor a nt
678 if (nt || callback_urls) {
679 goto cleanup;
680 }
681
682 // default lease
683 NPT_Int32 timeout = (NPT_Int32)*PLT_Constants::GetInstance().GetDefaultSubscribeLease().AsPointer();
684
685 // subscription renewed
686 // send the info to the service
687 service->ProcessRenewSubscription(context.GetLocalAddress(),
688 *sid,
689 timeout,
690 response);
691 return NPT_SUCCESS;
692 } else {
693 // new subscription ?
694 // verify nt is present and valid
695 if (!nt || nt->Compare("upnp:event", true)) {
696 response.SetStatus(412, "Precondition failed");
697 return NPT_SUCCESS;
698 }
699 // verify callback is present
700 if (!callback_urls) {
701 response.SetStatus(412, "Precondition failed");
702 return NPT_SUCCESS;
703 }
704
705 // default lease time
706 NPT_Int32 timeout = (NPT_Int32)*PLT_Constants::GetInstance().GetDefaultSubscribeLease().AsPointer();
707
708 // send the info to the service
709 service->ProcessNewSubscription(m_TaskManager,
710 context.GetLocalAddress(),
711 *callback_urls,
712 timeout,
713 response);
714 return NPT_SUCCESS;
715 }
716 } else if (method.Compare("UNSUBSCRIBE") == 0) {
717 // Do we have a sid ?
718 if (sid && sid->GetLength() > 0) {
719 // make sure we don't have a callback nor a nt
720 if (nt || callback_urls) {
721 goto cleanup;
722 }
723
724 // subscription cancelled
725 // send the info to the service
726 service->ProcessCancelSubscription(context.GetLocalAddress(),
727 *sid,
728 response);
729 return NPT_SUCCESS;
730 }
731
732 response.SetStatus(412, "Precondition failed");
733 return NPT_SUCCESS;
734 }
735
736 cleanup:
737 response.SetStatus(400, "Bad Request");
738 return NPT_SUCCESS;
739 }
740
741 /*----------------------------------------------------------------------
742 | PLT_DeviceHost::OnSsdpPacket
743 +---------------------------------------------------------------------*/
744 NPT_Result
OnSsdpPacket(const NPT_HttpRequest & request,const NPT_HttpRequestContext & context)745 PLT_DeviceHost::OnSsdpPacket(const NPT_HttpRequest& request,
746 const NPT_HttpRequestContext& context)
747 {
748 // get the address of who sent us some data back*/
749 NPT_String ip_address = context.GetRemoteAddress().GetIpAddress().ToString();
750 NPT_String method = request.GetMethod();
751 NPT_String url = request.GetUrl().ToRequestString(true);
752 NPT_String protocol = request.GetProtocol();
753 NPT_IpPort remote_port = context.GetRemoteAddress().GetPort();
754 const NPT_String* st = PLT_UPnPMessageHelper::GetST(request);
755
756 if (method.Compare("M-SEARCH") == 0) {
757 NPT_String prefix = NPT_String::Format("PLT_DeviceHost::OnSsdpPacket M-SEARCH for %s from %s:%d",
758 st?st->GetChars():"Unknown",
759 (const char*) ip_address, remote_port);
760 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINE, prefix, &request);
761
762 /*
763 // DLNA 7.2.3.5 support
764 if (remote_port < 1024 || remote_port == 1900) {
765 NPT_LOG_INFO_2("Ignoring M-SEARCH from %s:%d (invalid source port)",
766 (const char*) ip_address,
767 remote_port);
768 return NPT_FAILURE;
769 }
770 */
771
772 NPT_CHECK_POINTER_SEVERE(st);
773
774 if (url.Compare("*") || protocol.Compare("HTTP/1.1"))
775 return NPT_FAILURE;
776
777 const NPT_String* man = PLT_UPnPMessageHelper::GetMAN(request);
778 if (!man || man->Compare("\"ssdp:discover\"", true))
779 return NPT_FAILURE;
780
781 NPT_UInt32 mx;
782 NPT_CHECK_SEVERE(PLT_UPnPMessageHelper::GetMX(request, mx));
783
784 // create a task to respond to the request
785 NPT_TimeInterval timer((mx==0)?0.:(double)(NPT_System::GetRandomInteger()%(mx>5?5:mx)));
786 PLT_SsdpDeviceSearchResponseTask* task = new PLT_SsdpDeviceSearchResponseTask(this, context.GetRemoteAddress(), *st);
787 m_TaskManager->StartTask(task, &timer);
788 return NPT_SUCCESS;
789 } else {
790 NPT_String prefix = NPT_String::Format("Ignoring %s request from %s:%d",
791 method.GetChars(),
792 (const char*) ip_address, remote_port);
793 PLT_LOG_HTTP_REQUEST(NPT_LOG_LEVEL_FINE, prefix, &request);
794 }
795
796 return NPT_FAILURE;
797 }
798
799 /*----------------------------------------------------------------------
800 | PLT_DeviceHost::SendSsdpSearchResponse
801 +---------------------------------------------------------------------*/
802 NPT_Result
SendSsdpSearchResponse(PLT_DeviceData * device,NPT_HttpResponse & response,NPT_UdpSocket & socket,const char * st,const NPT_SocketAddress * addr)803 PLT_DeviceHost::SendSsdpSearchResponse(PLT_DeviceData* device,
804 NPT_HttpResponse& response,
805 NPT_UdpSocket& socket,
806 const char* st,
807 const NPT_SocketAddress* addr /* = NULL */)
808 {
809 // UPnP 1.1 BOOTID.UPNP.ORG header
810 PLT_UPnPMessageHelper::SetBootId(response, device->m_BootId);
811
812 // UPnP 1.1 CONFIGID.UPNP.ORG header
813 if (device->m_ConfigId > 0) {
814 PLT_UPnPMessageHelper::SetConfigId(response, device->m_ConfigId);
815 }
816
817 // ssdp:all or upnp:rootdevice
818 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
819 NPT_String::Compare(st, "upnp:rootdevice") == 0) {
820
821 if (device->m_ParentUUID.IsEmpty()) {
822 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
823
824 // upnp:rootdevice
825 PLT_SsdpSender::SendSsdp(response,
826 NPT_String("uuid:" + device->m_UUID + "::upnp:rootdevice"),
827 "upnp:rootdevice",
828 socket,
829 false,
830 addr);
831 }
832 }
833
834 // uuid:device-UUID
835 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
836 NPT_String::Compare(st, (const char*)("uuid:" + device->m_UUID)) == 0) {
837
838 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
839
840 // uuid:device-UUID
841 PLT_SsdpSender::SendSsdp(response,
842 "uuid:" + device->m_UUID,
843 "uuid:" + device->m_UUID,
844 socket,
845 false,
846 addr);
847 }
848
849 // urn:schemas-upnp-org:device:deviceType:ver
850 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
851 NPT_String::Compare(st, (const char*)(device->m_DeviceType)) == 0) {
852
853 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
854
855 // uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:ver
856 PLT_SsdpSender::SendSsdp(response,
857 NPT_String("uuid:" + device->m_UUID + "::" + device->m_DeviceType),
858 device->m_DeviceType,
859 socket,
860 false,
861 addr);
862 }
863
864 // services
865 for (int i=0; i < (int)device->m_Services.GetItemCount(); i++) {
866 if (NPT_String::Compare(st, "ssdp:all") == 0 ||
867 NPT_String::Compare(st, (const char*)(device->m_Services[i]->GetServiceType())) == 0) {
868
869 NPT_LOG_FINE_1("Responding to a M-SEARCH request for %s", st);
870
871 // uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:ver
872 PLT_SsdpSender::SendSsdp(response,
873 NPT_String("uuid:" + device->m_UUID + "::" + device->m_Services[i]->GetServiceType()),
874 device->m_Services[i]->GetServiceType(),
875 socket,
876 false,
877 addr);
878 }
879 }
880
881 // embedded devices
882 for (int j=0; j < (int)device->m_EmbeddedDevices.GetItemCount(); j++) {
883 SendSsdpSearchResponse(device->m_EmbeddedDevices[j].AsPointer(),
884 response,
885 socket,
886 st,
887 addr);
888 }
889
890 return NPT_SUCCESS;
891 }
892
893 /*----------------------------------------------------------------------
894 | PLT_DeviceHost::OnAction
895 +---------------------------------------------------------------------*/
896 NPT_Result
OnAction(PLT_ActionReference & action,const PLT_HttpRequestContext & context)897 PLT_DeviceHost::OnAction(PLT_ActionReference& action,
898 const PLT_HttpRequestContext& context)
899 {
900 NPT_COMPILER_UNUSED(context);
901 action->SetError(401, "Invalid Action");
902 return NPT_FAILURE;
903 }
904
905