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