1 /* MiniDLNA project
2  *
3  * http://sourceforge.net/projects/minidlna/
4  *
5  * MiniDLNA media server
6  * Copyright (C) 2008-2009  Justin Maggard
7  *
8  * This file is part of MiniDLNA.
9  *
10  * MiniDLNA is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  *
14  * MiniDLNA is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
21  *
22  * Portions of the code from the MiniUPnP project:
23  *
24  * Copyright (c) 2006-2007, Thomas Bernard
25  * All rights reserved.
26  *
27  * Redistribution and use in source and binary forms, with or without
28  * modification, are permitted provided that the following conditions are met:
29  *     * Redistributions of source code must retain the above copyright
30  *       notice, this list of conditions and the following disclaimer.
31  *     * Redistributions in binary form must reproduce the above copyright
32  *       notice, this list of conditions and the following disclaimer in the
33  *       documentation and/or other materials provided with the distribution.
34  *     * The name of the author may not be used to endorse or promote products
35  *       derived from this software without specific prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
38  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
41  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
47  * POSSIBILITY OF SUCH DAMAGE.
48  */
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <sys/param.h>
56 #include <ctype.h>
57 #include <sys/types.h>
58 #include <sys/stat.h>
59 #include <fcntl.h>
60 #include <errno.h>
61 #include <arpa/inet.h>
62 #include <sys/time.h>
63 #include <sys/resource.h>
64 #include <limits.h>
65 
66 #include "config.h"
67 #include "event.h"
68 #include "upnpglobalvars.h"
69 #include "upnphttp.h"
70 #include "upnpdescgen.h"
71 #include "minidlnapath.h"
72 #include "upnpsoap.h"
73 #include "upnpevents.h"
74 #include "utils.h"
75 #include "getifaddr.h"
76 #include "image_utils.h"
77 #include "log.h"
78 #include "sql.h"
79 #include <libexif/exif-loader.h>
80 #include "tivo_utils.h"
81 #include "tivo_commands.h"
82 #include "clients.h"
83 #include "process.h"
84 #include "sendfile.h"
85 
86 #define MAX_BUFFER_SIZE 2147483647
87 #define MIN_BUFFER_SIZE 65536
88 
89 #define INIT_STR(s, d) { s.data = d; s.size = sizeof(d); s.off = 0; }
90 
91 #include "icons.c"
92 
93 enum event_type {
94 	E_INVALID,
95 	E_SUBSCRIBE,
96 	E_RENEW
97 };
98 
99 static void SendResp_icon(struct upnphttp *, char * url);
100 static void SendResp_albumArt(struct upnphttp *, char * url);
101 static void SendResp_caption(struct upnphttp *, char * url);
102 static void SendResp_resizedimg(struct upnphttp *, char * url);
103 static void SendResp_thumbnail(struct upnphttp *, char * url);
104 static void SendResp_dlnafile(struct upnphttp *, char * url);
105 static void Process_upnphttp(struct event *ev);
106 
107 struct upnphttp *
New_upnphttp(int s)108 New_upnphttp(int s)
109 {
110 	struct upnphttp * ret;
111 	if(s<0)
112 		return NULL;
113 	ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
114 	if(ret == NULL)
115 		return NULL;
116 	memset(ret, 0, sizeof(struct upnphttp));
117 	ret->ev = (struct event ){ .fd = s, .rdwr = EVENT_READ, .process = Process_upnphttp, .data = ret };
118 	event_module.add(&ret->ev);
119 	return ret;
120 }
121 
122 void
CloseSocket_upnphttp(struct upnphttp * h)123 CloseSocket_upnphttp(struct upnphttp * h)
124 {
125 
126 	event_module.del(&h->ev, EV_FLAG_CLOSING);
127 	if(close(h->ev.fd) < 0)
128 	{
129 		DPRINTF(E_ERROR, L_HTTP, "CloseSocket_upnphttp: close(%d): %s\n", h->ev.fd, strerror(errno));
130 	}
131 	h->ev.fd = -1;
132 	h->state = 100;
133 }
134 
135 void
Delete_upnphttp(struct upnphttp * h)136 Delete_upnphttp(struct upnphttp * h)
137 {
138 	if(h)
139 	{
140 		if(h->ev.fd >= 0)
141 			CloseSocket_upnphttp(h);
142 		free(h->req_buf);
143 		free(h->res_buf);
144 		free(h);
145 	}
146 }
147 
148 /* parse HttpHeaders of the REQUEST */
149 static void
ParseHttpHeaders(struct upnphttp * h)150 ParseHttpHeaders(struct upnphttp * h)
151 {
152 	int client = 0;
153 	char * line;
154 	char * colon;
155 	char * p;
156 	int n;
157 	line = h->req_buf;
158 	/* TODO : check if req_buf, contentoff are ok */
159 	while(line < (h->req_buf + h->req_contentoff))
160 	{
161 		colon = strchr(line, ':');
162 		if(colon)
163 		{
164 			if(strncasecmp(line, "Content-Length", 14)==0)
165 			{
166 				p = colon;
167 				while(*p && (*p < '0' || *p > '9'))
168 					p++;
169 				h->req_contentlen = atoi(p);
170 				if(h->req_contentlen < 0) {
171 					DPRINTF(E_WARN, L_HTTP, "Invalid Content-Length %d", h->req_contentlen);
172 					h->req_contentlen = 0;
173 				}
174 			}
175 			else if(strncasecmp(line, "SOAPAction", 10)==0)
176 			{
177 				p = colon;
178 				n = 0;
179 				while(*p == ':' || *p == ' ' || *p == '\t')
180 					p++;
181 				while(p[n] >= ' ')
182 					n++;
183 				if(n >= 2 &&
184 				   ((p[0] == '"' && p[n-1] == '"') ||
185 				    (p[0] == '\'' && p[n-1] == '\'')))
186 				{
187 					p++;
188 					n -= 2;
189 				}
190 				h->req_soapAction = p;
191 				h->req_soapActionLen = n;
192 			}
193 			else if(strncasecmp(line, "Callback", 8)==0)
194 			{
195 				p = colon;
196 				while(*p && *p != '<' && *p != '\r' )
197 					p++;
198 				n = 0;
199 				while(p[n] && p[n] != '>' && p[n] != '\r' )
200 					n++;
201 				h->req_Callback = p + 1;
202 				h->req_CallbackLen = MAX(0, n - 1);
203 			}
204 			else if(strncasecmp(line, "SID", 3)==0)
205 			{
206 				//zqiu: fix bug for test 4.0.5
207 				//Skip extra headers like "SIDHEADER: xxxxxx xxx"
208 				for(p=line+3;p<colon;p++)
209 				{
210 					if(!isspace(*p))
211 					{
212 						p = NULL; //unexpected header
213 						break;
214 					}
215 				}
216 				if(p) {
217 					p = colon + 1;
218 					while(isspace(*p))
219 						p++;
220 					n = 0;
221 					while(p[n] && !isspace(p[n]))
222 						n++;
223 					h->req_SID = p;
224 					h->req_SIDLen = n;
225 				}
226 			}
227 			else if(strncasecmp(line, "NT", 2)==0)
228 			{
229 				p = colon + 1;
230 				while(isspace(*p))
231 					p++;
232 				n = 0;
233 				while(p[n] && !isspace(p[n]))
234 					n++;
235 				h->req_NT = p;
236 				h->req_NTLen = n;
237 			}
238 			/* Timeout: Seconds-nnnn */
239 			/* TIMEOUT
240 			Recommended. Requested duration until subscription expires,
241 			either number of seconds or infinite. Recommendation
242 			by a UPnP Forum working committee. Defined by UPnP vendor.
243 			Consists of the keyword "Second-" followed (without an
244 			intervening space) by either an integer or the keyword "infinite". */
245 			else if(strncasecmp(line, "Timeout", 7)==0)
246 			{
247 				p = colon + 1;
248 				while(isspace(*p))
249 					p++;
250 				if(strncasecmp(p, "Second-", 7)==0) {
251 					h->req_Timeout = atoi(p+7);
252 				}
253 			}
254 			// Range: bytes=xxx-yyy
255 			else if(strncasecmp(line, "Range", 5)==0)
256 			{
257 				p = colon + 1;
258 				while(isspace(*p))
259 					p++;
260 				if(strncasecmp(p, "bytes=", 6)==0) {
261 					h->reqflags |= FLAG_RANGE;
262 					h->req_RangeStart = strtoll(p+6, &colon, 10);
263 					h->req_RangeEnd = colon ? atoll(colon+1) : 0;
264 					DPRINTF(E_DEBUG, L_HTTP, "Range Start-End: %lld - %lld\n",
265 						(long long)h->req_RangeStart,
266 						h->req_RangeEnd ? (long long)h->req_RangeEnd : -1);
267 				}
268 			}
269 			else if(strncasecmp(line, "Host", 4)==0)
270 			{
271 				int i;
272 				h->reqflags |= FLAG_HOST;
273 				p = colon + 1;
274 				while(isspace(*p))
275 					p++;
276 				for(n = 0; n < n_lan_addr; n++)
277 				{
278 					for(i = 0; lan_addr[n].str[i]; i++)
279 					{
280 						if(lan_addr[n].str[i] != p[i])
281 							break;
282 					}
283 					if(i && !lan_addr[n].str[i])
284 					{
285 						h->iface = n;
286 						break;
287 					}
288 				}
289 			}
290 			else if(strncasecmp(line, "User-Agent", 10)==0)
291 			{
292 				int i;
293 				/* Skip client detection if we already detected it. */
294 				if( client )
295 					goto next_header;
296 				p = colon + 1;
297 				while(isspace(*p))
298 					p++;
299 				for (i = 0; client_types[i].name; i++)
300 				{
301 					if (client_types[i].match_type != EUserAgent)
302 						continue;
303 					if (strstrc(p, client_types[i].match, '\r') != NULL)
304 					{
305 						client = i;
306 						break;
307 					}
308 				}
309 			}
310 			else if(strncasecmp(line, "X-AV-Client-Info", 16)==0)
311 			{
312 				int i;
313 				/* Skip client detection if we already detected it. */
314 				if( client && client_types[client].type < EStandardDLNA150 )
315 					goto next_header;
316 				p = colon + 1;
317 				while(isspace(*p))
318 					p++;
319 				for (i = 0; client_types[i].name; i++)
320 				{
321 					if (client_types[i].match_type != EXAVClientInfo)
322 						continue;
323 					if (strstrc(p, client_types[i].match, '\r') != NULL)
324 					{
325 						client = i;
326 						break;
327 					}
328 				}
329 			}
330 			else if(strncasecmp(line, "Transfer-Encoding", 17)==0)
331 			{
332 				p = colon + 1;
333 				while(isspace(*p))
334 					p++;
335 				if(strncasecmp(p, "chunked", 7)==0)
336 				{
337 					h->reqflags |= FLAG_CHUNKED;
338 				}
339 			}
340 			else if(strncasecmp(line, "Accept-Language", 15)==0)
341 			{
342 				h->reqflags |= FLAG_LANGUAGE;
343 			}
344 			else if(strncasecmp(line, "getcontentFeatures.dlna.org", 27)==0)
345 			{
346 				p = colon + 1;
347 				while(isspace(*p))
348 					p++;
349 				if( (*p != '1') || !isspace(p[1]) )
350 					h->reqflags |= FLAG_INVALID_REQ;
351 			}
352 			else if(strncasecmp(line, "TimeSeekRange.dlna.org", 22)==0)
353 			{
354 				h->reqflags |= FLAG_TIMESEEK;
355 			}
356 			else if(strncasecmp(line, "PlaySpeed.dlna.org", 18)==0)
357 			{
358 				h->reqflags |= FLAG_PLAYSPEED;
359 			}
360 			else if(strncasecmp(line, "realTimeInfo.dlna.org", 21)==0)
361 			{
362 				h->reqflags |= FLAG_REALTIMEINFO;
363 			}
364 			else if(strncasecmp(line, "getAvailableSeekRange.dlna.org", 21)==0)
365 			{
366 				p = colon + 1;
367 				while(isspace(*p))
368 					p++;
369 				if( (*p != '1') || !isspace(p[1]) )
370 					h->reqflags |= FLAG_INVALID_REQ;
371 			}
372 			else if(strncasecmp(line, "transferMode.dlna.org", 21)==0)
373 			{
374 				p = colon + 1;
375 				while(isspace(*p))
376 					p++;
377 				if(strncasecmp(p, "Streaming", 9)==0)
378 				{
379 					h->reqflags |= FLAG_XFERSTREAMING;
380 				}
381 				if(strncasecmp(p, "Interactive", 11)==0)
382 				{
383 					h->reqflags |= FLAG_XFERINTERACTIVE;
384 				}
385 				if(strncasecmp(p, "Background", 10)==0)
386 				{
387 					h->reqflags |= FLAG_XFERBACKGROUND;
388 				}
389 			}
390 			else if(strncasecmp(line, "getCaptionInfo.sec", 18)==0)
391 			{
392 				h->reqflags |= FLAG_CAPTION;
393 			}
394 			else if(strncasecmp(line, "FriendlyName", 12)==0)
395 			{
396 				int i;
397 				p = colon + 1;
398 				while(isspace(*p))
399 					p++;
400 				for (i = 0; client_types[i].name; i++)
401 				{
402 					if (client_types[i].match_type != EFriendlyName)
403 						continue;
404 					if (strstrc(p, client_types[i].match, '\r') != NULL)
405 					{
406 						client = i;
407 						break;
408 					}
409 				}
410 			}
411 			else if(strncasecmp(line, "uctt.upnp.org:", 14)==0)
412 			{
413 				/* Conformance testing */
414 				SETFLAG(DLNA_STRICT_MASK);
415 			}
416 		}
417 next_header:
418 		line = strstr(line, "\r\n");
419 		if (!line)
420 			return;
421 		line += 2;
422 	}
423 	if (h->reqflags & FLAG_CHUNKED)
424 	{
425 		char *endptr;
426 		h->req_chunklen = -1;
427 		if (h->req_buflen <= h->req_contentoff)
428 			return;
429 		while( (line < (h->req_buf + h->req_buflen)) &&
430 		       (h->req_chunklen = strtol(line, &endptr, 16) > 0) &&
431 		       (endptr != line) )
432 		{
433 			endptr = strstr(endptr, "\r\n");
434 			if (!endptr)
435 			{
436 				return;
437 			}
438 			line = endptr+h->req_chunklen+2;
439 		}
440 
441 		if( endptr == line )
442 		{
443 			h->req_chunklen = -1;
444 			return;
445 		}
446 	}
447 	/* If the client type wasn't found, search the cache.
448 	 * This is done because a lot of clients like to send a
449 	 * different User-Agent with different types of requests. */
450 	h->req_client = SearchClientCache(h->clientaddr, 0);
451 	/* Add this client to the cache if it's not there already. */
452 	if (!h->req_client)
453 	{
454 		h->req_client = AddClientCache(h->clientaddr, client);
455 	}
456 	else if (client)
457 	{
458 		enum client_types type = client_types[client].type;
459 		enum client_types ctype = h->req_client->type->type;
460 		/* If we know the client and our new detection is generic, use our cached info */
461 		/* If we detected a Samsung Series B earlier, don't overwrite it with Series A info */
462 		if ((ctype && ctype < EStandardDLNA150 && type >= EStandardDLNA150) ||
463 		    (ctype == ESamsungSeriesB && type == ESamsungSeriesA))
464 			return;
465 		h->req_client->type = &client_types[client];
466 		h->req_client->age = time(NULL);
467 	}
468 }
469 
470 /* very minimalistic 400 error message */
471 static void
Send400(struct upnphttp * h)472 Send400(struct upnphttp * h)
473 {
474 	static const char body400[] =
475 		"<HTML><HEAD><TITLE>400 Bad Request</TITLE></HEAD>"
476 		"<BODY><H1>Bad Request</H1>The request is invalid"
477 		" for this HTTP version.</BODY></HTML>\r\n";
478 	h->respflags = FLAG_HTML;
479 	BuildResp2_upnphttp(h, 400, "Bad Request",
480 	                    body400, sizeof(body400) - 1);
481 	SendResp_upnphttp(h);
482 	CloseSocket_upnphttp(h);
483 }
484 
485 /* very minimalistic 403 error message */
486 static void
Send403(struct upnphttp * h)487 Send403(struct upnphttp * h)
488 {
489 	static const char body403[] =
490 		"<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>"
491 		"<BODY><H1>Forbidden</H1>You don't have permission to access this resource."
492 		"</BODY></HTML>\r\n";
493 	h->respflags = FLAG_HTML;
494 	BuildResp2_upnphttp(h, 403, "Forbidden",
495 	                    body403, sizeof(body403) - 1);
496 	SendResp_upnphttp(h);
497 	CloseSocket_upnphttp(h);
498 }
499 
500 /* very minimalistic 404 error message */
501 static void
Send404(struct upnphttp * h)502 Send404(struct upnphttp * h)
503 {
504 	static const char body404[] =
505 		"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
506 		"<BODY><H1>Not Found</H1>The requested URL was not found"
507 		" on this server.</BODY></HTML>\r\n";
508 	h->respflags = FLAG_HTML;
509 	BuildResp2_upnphttp(h, 404, "Not Found",
510 	                    body404, sizeof(body404) - 1);
511 	SendResp_upnphttp(h);
512 	CloseSocket_upnphttp(h);
513 }
514 
515 /* very minimalistic 406 error message */
516 static void
Send406(struct upnphttp * h)517 Send406(struct upnphttp * h)
518 {
519 	static const char body406[] =
520 		"<HTML><HEAD><TITLE>406 Not Acceptable</TITLE></HEAD>"
521 		"<BODY><H1>Not Acceptable</H1>An unsupported operation"
522 		" was requested.</BODY></HTML>\r\n";
523 	h->respflags = FLAG_HTML;
524 	BuildResp2_upnphttp(h, 406, "Not Acceptable",
525 	                    body406, sizeof(body406) - 1);
526 	SendResp_upnphttp(h);
527 	CloseSocket_upnphttp(h);
528 }
529 
530 /* very minimalistic 416 error message */
531 static void
Send416(struct upnphttp * h)532 Send416(struct upnphttp * h)
533 {
534 	static const char body416[] =
535 		"<HTML><HEAD><TITLE>416 Requested Range Not Satisfiable</TITLE></HEAD>"
536 		"<BODY><H1>Requested Range Not Satisfiable</H1>The requested range"
537 		" was outside the file's size.</BODY></HTML>\r\n";
538 	h->respflags = FLAG_HTML;
539 	BuildResp2_upnphttp(h, 416, "Requested Range Not Satisfiable",
540 	                    body416, sizeof(body416) - 1);
541 	SendResp_upnphttp(h);
542 	CloseSocket_upnphttp(h);
543 }
544 
545 /* very minimalistic 500 error message */
546 void
Send500(struct upnphttp * h)547 Send500(struct upnphttp * h)
548 {
549 	static const char body500[] =
550 		"<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>"
551 		"<BODY><H1>Internal Server Error</H1>Server encountered "
552 		"and Internal Error.</BODY></HTML>\r\n";
553 	h->respflags = FLAG_HTML;
554 	BuildResp2_upnphttp(h, 500, "Internal Server Errror",
555 	                    body500, sizeof(body500) - 1);
556 	SendResp_upnphttp(h);
557 	CloseSocket_upnphttp(h);
558 }
559 
560 /* very minimalistic 501 error message */
561 void
Send501(struct upnphttp * h)562 Send501(struct upnphttp * h)
563 {
564 	static const char body501[] =
565 		"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
566 		"<BODY><H1>Not Implemented</H1>The HTTP Method "
567 		"is not implemented by this server.</BODY></HTML>\r\n";
568 	h->respflags = FLAG_HTML;
569 	BuildResp2_upnphttp(h, 501, "Not Implemented",
570 	                    body501, sizeof(body501) - 1);
571 	SendResp_upnphttp(h);
572 	CloseSocket_upnphttp(h);
573 }
574 
575 /* Sends the description generated by the parameter */
576 static void
sendXMLdesc(struct upnphttp * h,char * (f)(int *))577 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
578 {
579 	char * desc;
580 	int len;
581 	desc = f(&len);
582 	if(!desc)
583 	{
584 		DPRINTF(E_ERROR, L_HTTP, "Failed to generate XML description\n");
585 		Send500(h);
586 		return;
587 	}
588 	BuildResp_upnphttp(h, desc, len);
589 	SendResp_upnphttp(h);
590 	CloseSocket_upnphttp(h);
591 	free(desc);
592 }
593 
594 #ifdef READYNAS
595 static void
SendResp_readynas_admin(struct upnphttp * h)596 SendResp_readynas_admin(struct upnphttp * h)
597 {
598 	char body[128];
599 	int l;
600 
601 	h->respflags = FLAG_HTML;
602 	l = snprintf(body, sizeof(body), "<meta http-equiv=\"refresh\" content=\"0; url=https://%s/admin/\">",
603 	              lan_addr[h->iface].str);
604 
605 	BuildResp_upnphttp(h, body, l);
606 	SendResp_upnphttp(h);
607 	CloseSocket_upnphttp(h);
608 }
609 #endif
610 
611 static void
SendResp_presentation(struct upnphttp * h)612 SendResp_presentation(struct upnphttp * h)
613 {
614 	struct string_s str;
615 	char body[4096];
616 	int a, v, p, i;
617 
618 	INIT_STR(str, body);
619 
620 	h->respflags = FLAG_HTML;
621 
622 	a = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'a*'");
623 	v = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'v*'");
624 	p = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'i*'");
625 	strcatf(&str,
626 		"<HTML><HEAD><TITLE>" SERVER_NAME " " MINIDLNA_VERSION "</TITLE></HEAD>"
627 		"<BODY><div style=\"text-align: center\">"
628 		"<h2>" SERVER_NAME " status</h2></div>");
629 
630 	strcatf(&str,
631 		"<h3>Media library</h3>"
632 		"<table border=1 cellpadding=10>"
633 		"<tr><td>Audio files</td><td>%d</td></tr>"
634 		"<tr><td>Video files</td><td>%d</td></tr>"
635 		"<tr><td>Image files</td><td>%d</td></tr>"
636 		"</table>", a, v, p);
637 
638 	if (GETFLAG(SCANNING_MASK))
639 		strcatf(&str,
640 			"<br><i>* Media scan in progress</i><br>");
641 
642 	strcatf(&str,
643 		"<h3>Connected clients</h3>"
644 		"<table border=1 cellpadding=10>"
645 		"<tr><td>ID</td><td>Type</td><td>IP Address</td><td>HW Address</td><td>Connections</td></tr>");
646 	for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
647 	{
648 		if (!clients[i].addr.s_addr)
649 			continue;
650 		strcatf(&str, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%02X:%02X:%02X:%02X:%02X:%02X</td><td>%d</td></tr>",
651 				i, clients[i].type->name, inet_ntoa(clients[i].addr),
652 				clients[i].mac[0], clients[i].mac[1], clients[i].mac[2],
653 				clients[i].mac[3], clients[i].mac[4], clients[i].mac[5], clients[i].connections);
654 	}
655 	strcatf(&str, "</table>");
656 
657 	strcatf(&str, "<br>%d connection%s currently open<br>", number_of_children, (number_of_children == 1 ? "" : "s"));
658 	strcatf(&str, "</BODY></HTML>\r\n");
659 
660 	BuildResp_upnphttp(h, str.data, str.off);
661 	SendResp_upnphttp(h);
662 	CloseSocket_upnphttp(h);
663 }
664 
665 /* ProcessHTTPPOST_upnphttp()
666  * executes the SOAP query if it is possible */
667 static void
ProcessHTTPPOST_upnphttp(struct upnphttp * h)668 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
669 {
670 	if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
671 	{
672 		if(h->req_soapAction)
673 		{
674 			/* we can process the request */
675 			DPRINTF(E_DEBUG, L_HTTP, "SOAPAction: %.*s\n", h->req_soapActionLen, h->req_soapAction);
676 			ExecuteSoapAction(h,
677 				h->req_soapAction,
678 				h->req_soapActionLen);
679 		}
680 		else
681 		{
682 			static const char err400str[] =
683 				"<html><body>Bad request</body></html>";
684 			DPRINTF(E_WARN, L_HTTP, "No SOAPAction in HTTP headers\n");
685 			h->respflags = FLAG_HTML;
686 			BuildResp2_upnphttp(h, 400, "Bad Request",
687 			                    err400str, sizeof(err400str) - 1);
688 			SendResp_upnphttp(h);
689 			CloseSocket_upnphttp(h);
690 		}
691 	}
692 	else
693 	{
694 		/* waiting for remaining data */
695 		h->state = 1;
696 	}
697 }
698 
699 static int
check_event(struct upnphttp * h)700 check_event(struct upnphttp *h)
701 {
702 	enum event_type type = E_INVALID;
703 
704 	if (h->req_Callback)
705 	{
706 		if (h->req_SID || !h->req_NT)
707 		{
708 			BuildResp2_upnphttp(h, 400, "Bad Request",
709 				            "<html><body>Bad request</body></html>", 37);
710 		}
711 		else if (strncmp(h->req_Callback, "http://", 7) != 0 ||
712 		         strncmp(h->req_NT, "upnp:event", h->req_NTLen) != 0)
713 		{
714 			/* Missing or invalid CALLBACK : 412 Precondition Failed.
715 			 * If CALLBACK header is missing or does not contain a valid HTTP URL,
716 			 * the publisher must respond with HTTP error 412 Precondition Failed*/
717 			BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
718 		}
719 		else
720 		{
721 			/* Make sure callback URL points to the originating IP */
722 			struct in_addr addr;
723 			char addrstr[16];
724 			int i = 0;
725 			const char *p = h->req_Callback + 7;
726 			while (!strchr("/:>", *p) && i < sizeof(addrstr) - 1 &&
727 			       p < (h->req_Callback + h->req_CallbackLen))
728 			{
729 				addrstr[i++] = *(p++);
730 			}
731 			addrstr[i] = '\0';
732 
733 			if (inet_pton(AF_INET, addrstr, &addr) <= 0 ||
734 			    memcmp(&addr, &h->clientaddr, sizeof(struct in_addr)))
735 			{
736 				DPRINTF(E_ERROR, L_HTTP, "Bad callback IP (%s)\n", addrstr);
737 				BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
738 			}
739 			else
740 				type = E_SUBSCRIBE;
741 		}
742 	}
743 	else if (h->req_SID)
744 	{
745 		/* subscription renew */
746 		if (h->req_NT)
747 		{
748 			BuildResp2_upnphttp(h, 400, "Bad Request",
749 				            "<html><body>Bad request</body></html>", 37);
750 		}
751 		else
752 			type = E_RENEW;
753 	}
754 	else
755 	{
756 		BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
757 	}
758 
759 	return type;
760 }
761 
762 static void
ProcessHTTPSubscribe_upnphttp(struct upnphttp * h,const char * path)763 ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
764 {
765 	const char * sid;
766 	enum event_type type;
767 	DPRINTF(E_DEBUG, L_HTTP, "ProcessHTTPSubscribe %s\n", path);
768 	DPRINTF(E_DEBUG, L_HTTP, "Callback '%.*s' Timeout=%d\n",
769 		h->req_CallbackLen, h->req_Callback, h->req_Timeout);
770 	DPRINTF(E_DEBUG, L_HTTP, "SID '%.*s'\n", h->req_SIDLen, h->req_SID);
771 
772 	type = check_event(h);
773 	if (type == E_SUBSCRIBE)
774 	{
775 		/* - add to the subscriber list
776 		 * - respond HTTP/x.x 200 OK
777 		 * - Send the initial event message */
778 		/* Server:, SID:; Timeout: Second-(xx|infinite) */
779 		sid = upnpevents_addSubscriber(path, h->req_Callback,
780 		                               h->req_CallbackLen, h->req_Timeout);
781 		h->respflags = FLAG_TIMEOUT;
782 		if (sid)
783 		{
784 			DPRINTF(E_DEBUG, L_HTTP, "generated sid=%s\n", sid);
785 			h->respflags |= FLAG_SID;
786 			h->req_SID = sid;
787 			h->req_SIDLen = strlen(sid);
788 		}
789 		BuildResp_upnphttp(h, 0, 0);
790 	}
791 	else if (type == E_RENEW)
792 	{
793 		/* subscription renew */
794 		if (renewSubscription(h->req_SID, h->req_SIDLen, h->req_Timeout) < 0)
795 		{
796 			/* Invalid SID
797 			   412 Precondition Failed. If a SID does not correspond to a known,
798 			   un-expired subscription, the publisher must respond
799 			   with HTTP error 412 Precondition Failed. */
800 			BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
801 		}
802 		else
803 		{
804 			/* A DLNA device must enforce a 5 minute timeout */
805 			h->respflags = FLAG_TIMEOUT;
806 			h->req_Timeout = 300;
807 			h->respflags |= FLAG_SID;
808 			BuildResp_upnphttp(h, 0, 0);
809 		}
810 	}
811 	SendResp_upnphttp(h);
812 	CloseSocket_upnphttp(h);
813 }
814 
815 static void
ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h,const char * path)816 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
817 {
818 	enum event_type type;
819 	DPRINTF(E_DEBUG, L_HTTP, "ProcessHTTPUnSubscribe %s\n", path);
820 	DPRINTF(E_DEBUG, L_HTTP, "SID '%.*s'\n", h->req_SIDLen, h->req_SID);
821 	/* Remove from the list */
822 	type = check_event(h);
823 	if (type != E_INVALID)
824 	{
825 		if(upnpevents_removeSubscriber(h->req_SID, h->req_SIDLen) < 0)
826 			BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
827 		else
828 			BuildResp_upnphttp(h, 0, 0);
829 	}
830 	SendResp_upnphttp(h);
831 	CloseSocket_upnphttp(h);
832 }
833 
834 /* Parse and process Http Query
835  * called once all the HTTP headers have been received. */
836 static void
ProcessHttpQuery_upnphttp(struct upnphttp * h)837 ProcessHttpQuery_upnphttp(struct upnphttp * h)
838 {
839 	char HttpCommand[16];
840 	char HttpUrl[512];
841 	char * HttpVer;
842 	char * p;
843 	int i;
844 	p = h->req_buf;
845 	if(!p)
846 		return;
847 	for(i = 0; i<15 && *p && *p != ' ' && *p != '\r'; i++)
848 		HttpCommand[i] = *(p++);
849 	HttpCommand[i] = '\0';
850 	while(*p==' ')
851 		p++;
852 	for(i = 0; i<511 && *p && *p != ' ' && *p != '\r'; i++)
853 		HttpUrl[i] = *(p++);
854 	HttpUrl[i] = '\0';
855 	while(*p==' ')
856 		p++;
857 	HttpVer = h->HttpVer;
858 	for(i = 0; i<15 && *p && *p != '\r'; i++)
859 		HttpVer[i] = *(p++);
860 	HttpVer[i] = '\0';
861 
862 	/* set the interface here initially, in case there is no Host header */
863 	for(i = 0; i<n_lan_addr; i++)
864 	{
865 		if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
866 		   == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
867 		{
868 			h->iface = i;
869 			break;
870 		}
871 	}
872 
873 	ParseHttpHeaders(h);
874 
875 	/* see if we need to wait for remaining data */
876 	if( (h->reqflags & FLAG_CHUNKED) )
877 	{
878 		if( h->req_chunklen == -1)
879 		{
880 			Send400(h);
881 			return;
882 		}
883 		if( h->req_chunklen )
884 		{
885 			h->state = 2;
886 			return;
887 		}
888 		char *chunkstart, *chunk, *endptr, *endbuf;
889 		chunk = endbuf = chunkstart = h->req_buf + h->req_contentoff;
890 
891 		while ((h->req_chunklen = strtol(chunk, &endptr, 16)) > 0 && (endptr != chunk) )
892 		{
893 			endptr = strstr(endptr, "\r\n");
894 			if (!endptr)
895 			{
896 				Send400(h);
897 				return;
898 			}
899 			endptr += 2;
900 
901 			memmove(endbuf, endptr, h->req_chunklen);
902 
903 			endbuf += h->req_chunklen;
904 			chunk = endptr + h->req_chunklen;
905 		}
906 		h->req_contentlen = endbuf - chunkstart;
907 		h->req_buflen = endbuf - h->req_buf;
908 		h->state = 100;
909 	}
910 
911 	DPRINTF(E_DEBUG, L_HTTP, "HTTP REQUEST: %.*s\n", h->req_buflen, h->req_buf);
912 	if(strcmp("POST", HttpCommand) == 0)
913 	{
914 		h->req_command = EPost;
915 		ProcessHTTPPOST_upnphttp(h);
916 	}
917 	else if((strcmp("GET", HttpCommand) == 0) || (strcmp("HEAD", HttpCommand) == 0))
918 	{
919 		if( ((strcmp(h->HttpVer, "HTTP/1.1")==0) && !(h->reqflags & FLAG_HOST)) || (h->reqflags & FLAG_INVALID_REQ) )
920 		{
921 			DPRINTF(E_WARN, L_HTTP, "Invalid request, responding ERROR 400.  (No Host specified in HTTP headers?)\n");
922 			Send400(h);
923 			return;
924 		}
925 		/* 7.3.33.4 */
926 		else if( (h->reqflags & (FLAG_TIMESEEK|FLAG_PLAYSPEED)) &&
927 		         !(h->reqflags & FLAG_RANGE) )
928 		{
929 			DPRINTF(E_WARN, L_HTTP, "DLNA %s requested, responding ERROR 406\n",
930 				h->reqflags&FLAG_TIMESEEK ? "TimeSeek" : "PlaySpeed");
931 			Send406(h);
932 			return;
933 		}
934 		else if(strcmp("GET", HttpCommand) == 0)
935 		{
936 			h->req_command = EGet;
937 		}
938 		else
939 		{
940 			h->req_command = EHead;
941 		}
942 		if(strcmp(ROOTDESC_PATH, HttpUrl) == 0)
943 		{
944 			/* If it's a Xbox360, we might need a special friendly_name to be recognized */
945 			if( h->req_client && h->req_client->type->type == EXbox )
946 			{
947 				char model_sav[2];
948 				i = 0;
949 				memcpy(model_sav, modelnumber, 2);
950 				strcpy(modelnumber, "1");
951 				if( !strchr(friendly_name, ':') )
952 				{
953 					i = strlen(friendly_name);
954 					snprintf(friendly_name+i, FRIENDLYNAME_MAX_LEN-i, ": 1");
955 				}
956 				sendXMLdesc(h, genRootDesc);
957 				if( i )
958 					friendly_name[i] = '\0';
959 				memcpy(modelnumber, model_sav, 2);
960 			}
961 			else if( h->req_client && h->req_client->type->flags & FLAG_SAMSUNG_DCM10 )
962 			{
963 				sendXMLdesc(h, genRootDescSamsung);
964 			}
965 			else
966 			{
967 				sendXMLdesc(h, genRootDesc);
968 			}
969 		}
970 		else if(strcmp(CONTENTDIRECTORY_PATH, HttpUrl) == 0)
971 		{
972 			sendXMLdesc(h, genContentDirectory);
973 		}
974 		else if(strcmp(CONNECTIONMGR_PATH, HttpUrl) == 0)
975 		{
976 			sendXMLdesc(h, genConnectionManager);
977 		}
978 		else if(strcmp(X_MS_MEDIARECEIVERREGISTRAR_PATH, HttpUrl) == 0)
979 		{
980 			sendXMLdesc(h, genX_MS_MediaReceiverRegistrar);
981 		}
982 		else if(strncmp(HttpUrl, "/MediaItems/", 12) == 0)
983 		{
984 			SendResp_dlnafile(h, HttpUrl+12);
985 		}
986 		else if(strncmp(HttpUrl, "/Thumbnails/", 12) == 0)
987 		{
988 			SendResp_thumbnail(h, HttpUrl+12);
989 		}
990 		else if(strncmp(HttpUrl, "/AlbumArt/", 10) == 0)
991 		{
992 			SendResp_albumArt(h, HttpUrl+10);
993 		}
994 		#ifdef TIVO_SUPPORT
995 		else if(strncmp(HttpUrl, "/TiVoConnect", 12) == 0)
996 		{
997 			if( GETFLAG(TIVO_MASK) )
998 			{
999 				if( *(HttpUrl+12) == '?' )
1000 				{
1001 					ProcessTiVoCommand(h, HttpUrl+13);
1002 				}
1003 				else
1004 				{
1005 					DPRINTF(E_WARN, L_HTTP, "Invalid TiVo request! %s\n", HttpUrl+12);
1006 					Send404(h);
1007 				}
1008 			}
1009 			else
1010 			{
1011 				DPRINTF(E_WARN, L_HTTP, "TiVo request with out TiVo support enabled! %s\n",
1012 					HttpUrl+12);
1013 				Send404(h);
1014 			}
1015 		}
1016 		#endif
1017 		else if(strncmp(HttpUrl, "/Resized/", 9) == 0)
1018 		{
1019 			SendResp_resizedimg(h, HttpUrl+9);
1020 		}
1021 		else if(strncmp(HttpUrl, "/icons/", 7) == 0)
1022 		{
1023 			SendResp_icon(h, HttpUrl+7);
1024 		}
1025 		else if(strncmp(HttpUrl, "/Captions/", 10) == 0)
1026 		{
1027 			SendResp_caption(h, HttpUrl+10);
1028 		}
1029 		else if(strncmp(HttpUrl, "/status", 7) == 0)
1030 		{
1031 			SendResp_presentation(h);
1032 		}
1033 		else if(strcmp(HttpUrl, "/") == 0)
1034 		{
1035 			#ifdef READYNAS
1036 			SendResp_readynas_admin(h);
1037 			#else
1038 			SendResp_presentation(h);
1039 			#endif
1040 		}
1041 		else
1042 		{
1043 			DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", HttpUrl);
1044 			Send404(h);
1045 		}
1046 	}
1047 	else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
1048 	{
1049 		h->req_command = ESubscribe;
1050 		ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
1051 	}
1052 	else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
1053 	{
1054 		h->req_command = EUnSubscribe;
1055 		ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
1056 	}
1057 	else
1058 	{
1059 		DPRINTF(E_WARN, L_HTTP, "Unsupported HTTP Command %s\n", HttpCommand);
1060 		Send501(h);
1061 	}
1062 }
1063 
1064 static void
Process_upnphttp(struct event * ev)1065 Process_upnphttp(struct event *ev)
1066 {
1067 	char buf[2048];
1068 	struct upnphttp *h = ev->data;
1069 	int n;
1070 
1071 	switch(h->state)
1072 	{
1073 	case 0:
1074 		n = recv(h->ev.fd, buf, 2048, 0);
1075 		if(n<0)
1076 		{
1077 			DPRINTF(E_ERROR, L_HTTP, "recv (state0): %s\n", strerror(errno));
1078 			h->state = 100;
1079 		}
1080 		else if(n==0)
1081 		{
1082 			DPRINTF(E_DEBUG, L_HTTP, "HTTP Connection closed unexpectedly\n");
1083 			h->state = 100;
1084 		}
1085 		else
1086 		{
1087 			int new_req_buflen;
1088 			const char * endheaders;
1089 			/* if 1st arg of realloc() is null,
1090 			 * realloc behaves the same as malloc() */
1091 			new_req_buflen = n + h->req_buflen + 1;
1092 			if (new_req_buflen >= 1024 * 1024)
1093 			{
1094 				DPRINTF(E_ERROR, L_HTTP, "Receive headers too large (received %d bytes)\n", new_req_buflen);
1095 				h->state = 100;
1096 				break;
1097 			}
1098 			h->req_buf = (char *)realloc(h->req_buf, new_req_buflen);
1099 			if (!h->req_buf)
1100 			{
1101 				DPRINTF(E_ERROR, L_HTTP, "Receive headers: %s\n", strerror(errno));
1102 				h->state = 100;
1103 				break;
1104 			}
1105 			memcpy(h->req_buf + h->req_buflen, buf, n);
1106 			h->req_buflen += n;
1107 			h->req_buf[h->req_buflen] = '\0';
1108 			/* search for the string "\r\n\r\n" */
1109 			endheaders = strstr(h->req_buf, "\r\n\r\n");
1110 			if(endheaders)
1111 			{
1112 				h->req_contentoff = endheaders - h->req_buf + 4;
1113 				h->req_contentlen = h->req_buflen - h->req_contentoff;
1114 				ProcessHttpQuery_upnphttp(h);
1115 			}
1116 		}
1117 		break;
1118 	case 1:
1119 	case 2:
1120 		n = recv(h->ev.fd, buf, sizeof(buf), 0);
1121 		if(n < 0)
1122 		{
1123 			DPRINTF(E_ERROR, L_HTTP, "recv (state%d): %s\n", h->state, strerror(errno));
1124 			h->state = 100;
1125 		}
1126 		else if(n == 0)
1127 		{
1128 			DPRINTF(E_DEBUG, L_HTTP, "HTTP Connection closed unexpectedly\n");
1129 			h->state = 100;
1130 		}
1131 		else
1132 		{
1133 			buf[sizeof(buf)-1] = '\0';
1134 			/*fwrite(buf, 1, n, stdout);*/	/* debug */
1135 			h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen);
1136 			if (!h->req_buf)
1137 			{
1138 				DPRINTF(E_ERROR, L_HTTP, "Receive request body: %s\n", strerror(errno));
1139 				h->state = 100;
1140 				break;
1141 			}
1142 			memcpy(h->req_buf + h->req_buflen, buf, n);
1143 			h->req_buflen += n;
1144 			if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
1145 			{
1146 				/* Need the struct to point to the realloc'd memory locations */
1147 				if( h->state == 1 )
1148 				{
1149 					ParseHttpHeaders(h);
1150 					ProcessHTTPPOST_upnphttp(h);
1151 				}
1152 				else if( h->state == 2 )
1153 				{
1154 					ProcessHttpQuery_upnphttp(h);
1155 				}
1156 			}
1157 		}
1158 		break;
1159 	default:
1160 		DPRINTF(E_WARN, L_HTTP, "Unexpected state: %d\n", h->state);
1161 	}
1162 }
1163 
1164 /* with response code and response message
1165  * also allocate enough memory */
1166 
1167 void
BuildHeader_upnphttp(struct upnphttp * h,int respcode,const char * respmsg,int bodylen)1168 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
1169                      const char * respmsg,
1170                      int bodylen)
1171 {
1172 	static const char httpresphead[] =
1173 		"%s %d %s\r\n"
1174 		"Content-Type: %s\r\n"
1175 		"Connection: close\r\n"
1176 		"Content-Length: %d\r\n"
1177 		"Server: " MINIDLNA_SERVER_STRING "\r\n";
1178 	time_t curtime = time(NULL);
1179 	char date[30];
1180 	int templen;
1181 	struct string_s res;
1182 	if(!h->res_buf)
1183 	{
1184 		templen = sizeof(httpresphead) + 256 + bodylen;
1185 		h->res_buf = (char *)malloc(templen);
1186 		h->res_buf_alloclen = templen;
1187 	}
1188 	res.data = h->res_buf;
1189 	res.size = h->res_buf_alloclen;
1190 	res.off = 0;
1191 	strcatf(&res, httpresphead, "HTTP/1.1",
1192 	              respcode, respmsg,
1193 	              (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
1194 							 bodylen);
1195 	/* Additional headers */
1196 	if(h->respflags & FLAG_TIMEOUT) {
1197 		strcatf(&res, "Timeout: Second-");
1198 		if(h->req_Timeout) {
1199 			strcatf(&res, "%d\r\n", h->req_Timeout);
1200 		} else {
1201 			strcatf(&res, "300\r\n");
1202 		}
1203 	}
1204 	if(h->respflags & FLAG_SID) {
1205 		strcatf(&res, "SID: %.*s\r\n", h->req_SIDLen, h->req_SID);
1206 	}
1207 	if(h->reqflags & FLAG_LANGUAGE) {
1208 		strcatf(&res, "Content-Language: en\r\n");
1209 	}
1210 	strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
1211 	strcatf(&res, "Date: %s\r\n", date);
1212 	strcatf(&res, "EXT:\r\n");
1213 	strcatf(&res, "\r\n");
1214 	h->res_buflen = res.off;
1215 	if(h->res_buf_alloclen < (h->res_buflen + bodylen))
1216 	{
1217 		h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
1218 		h->res_buf_alloclen = h->res_buflen + bodylen;
1219 	}
1220 }
1221 
1222 void
BuildResp2_upnphttp(struct upnphttp * h,int respcode,const char * respmsg,const char * body,int bodylen)1223 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
1224                     const char * respmsg,
1225                     const char * body, int bodylen)
1226 {
1227 	BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
1228 	if( h->req_command == EHead )
1229 		return;
1230 	if(body)
1231 		memcpy(h->res_buf + h->res_buflen, body, bodylen);
1232 	h->res_buflen += bodylen;
1233 }
1234 
1235 /* responding 200 OK ! */
1236 void
BuildResp_upnphttp(struct upnphttp * h,const char * body,int bodylen)1237 BuildResp_upnphttp(struct upnphttp *h, const char *body, int bodylen)
1238 {
1239 	BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
1240 }
1241 
1242 void
SendResp_upnphttp(struct upnphttp * h)1243 SendResp_upnphttp(struct upnphttp * h)
1244 {
1245 	int n;
1246 	DPRINTF(E_DEBUG, L_HTTP, "HTTP RESPONSE: %.*s\n", h->res_buflen, h->res_buf);
1247 	n = send(h->ev.fd, h->res_buf, h->res_buflen, 0);
1248 	if(n<0)
1249 	{
1250 		DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %s\n", strerror(errno));
1251 	}
1252 	else if(n < h->res_buflen)
1253 	{
1254 		/* TODO : handle correctly this case */
1255 		DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %d bytes sent (out of %d)\n",
1256 						n, h->res_buflen);
1257 	}
1258 }
1259 
1260 static int
send_data(struct upnphttp * h,char * header,size_t size,int flags)1261 send_data(struct upnphttp * h, char * header, size_t size, int flags)
1262 {
1263 	int n;
1264 
1265 	n = send(h->ev.fd, header, size, flags);
1266 	if(n<0)
1267 	{
1268 		DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %s\n", strerror(errno));
1269 	}
1270 	else if(n < h->res_buflen)
1271 	{
1272 		/* TODO : handle correctly this case */
1273 		DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %d bytes sent (out of %d)\n",
1274 						n, h->res_buflen);
1275 	}
1276 	else
1277 	{
1278 		return 0;
1279 	}
1280 	return 1;
1281 }
1282 
1283 static void
send_file(struct upnphttp * h,int sendfd,off_t offset,off_t end_offset)1284 send_file(struct upnphttp * h, int sendfd, off_t offset, off_t end_offset)
1285 {
1286 	off_t send_size;
1287 	off_t ret;
1288 	char *buf = NULL;
1289 #if HAVE_SENDFILE
1290 	int try_sendfile = 1;
1291 #endif
1292 
1293 	while( offset <= end_offset )
1294 	{
1295 #if HAVE_SENDFILE
1296 		if( try_sendfile )
1297 		{
1298 			send_size = ( ((end_offset - offset) < MAX_BUFFER_SIZE) ? (end_offset - offset + 1) : MAX_BUFFER_SIZE);
1299 			ret = sys_sendfile(h->ev.fd, sendfd, &offset, send_size);
1300 			if( ret == -1 )
1301 			{
1302 				DPRINTF(E_DEBUG, L_HTTP, "sendfile error :: error no. %d [%s]\n", errno, strerror(errno));
1303 				/* If sendfile isn't supported on the filesystem, don't bother trying to use it again. */
1304 				if( errno == EOVERFLOW || errno == EINVAL )
1305 					try_sendfile = 0;
1306 				else if( errno != EAGAIN )
1307 					break;
1308 			}
1309 			else
1310 			{
1311 				//DPRINTF(E_DEBUG, L_HTTP, "sent %lld bytes to %d. offset is now %lld.\n", ret, h->socket, offset);
1312 				continue;
1313 			}
1314 		}
1315 #endif
1316 		/* Fall back to regular I/O */
1317 		if( !buf )
1318 			buf = malloc(MIN_BUFFER_SIZE);
1319 		send_size = (((end_offset - offset) < MIN_BUFFER_SIZE) ? (end_offset - offset + 1) : MIN_BUFFER_SIZE);
1320 		lseek(sendfd, offset, SEEK_SET);
1321 		ret = read(sendfd, buf, send_size);
1322 		if( ret == -1 ) {
1323 			DPRINTF(E_DEBUG, L_HTTP, "read error :: error no. %d [%s]\n", errno, strerror(errno));
1324 			if( errno == EAGAIN )
1325 				continue;
1326 			else
1327 				break;
1328 		}
1329 		ret = write(h->ev.fd, buf, ret);
1330 		if( ret == -1 ) {
1331 			DPRINTF(E_DEBUG, L_HTTP, "write error :: error no. %d [%s]\n", errno, strerror(errno));
1332 			if( errno == EAGAIN )
1333 				continue;
1334 			else
1335 				break;
1336 		}
1337 		offset += ret;
1338 	}
1339 	free(buf);
1340 }
1341 
1342 static void
start_dlna_header(struct string_s * str,int respcode,const char * tmode,const char * mime)1343 start_dlna_header(struct string_s *str, int respcode, const char *tmode, const char *mime)
1344 {
1345 	char date[30];
1346 	time_t now;
1347 
1348 	now = time(NULL);
1349 	strftime(date, sizeof(date),"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&now));
1350 	strcatf(str, "HTTP/1.1 %d OK\r\n"
1351 	             "Connection: close\r\n"
1352 	             "Date: %s\r\n"
1353 	             "Server: " MINIDLNA_SERVER_STRING "\r\n"
1354 	             "EXT:\r\n"
1355 	             "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
1356 	             "transferMode.dlna.org: %s\r\n"
1357 	             "Content-Type: %s\r\n",
1358 	             respcode, date, tmode, mime);
1359 }
1360 
1361 static int
_open_file(const char * orig_path)1362 _open_file(const char *orig_path)
1363 {
1364 	struct media_dir_s *media_path;
1365 	char buf[PATH_MAX];
1366 	const char *path;
1367 	int fd;
1368 
1369 	if (!GETFLAG(WIDE_LINKS_MASK))
1370 	{
1371 		path = realpath(orig_path, buf);
1372 		if (!path)
1373 		{
1374 			DPRINTF(E_ERROR, L_HTTP, "Error resolving path %s: %s\n",
1375 						orig_path, strerror(errno));
1376 			return -1;
1377 		}
1378 
1379 		for (media_path = media_dirs; media_path; media_path = media_path->next)
1380 		{
1381 			if (strncmp(path, media_path->path, strlen(media_path->path)) == 0)
1382 				break;
1383 		}
1384 		if (!media_path && strncmp(path, db_path, strlen(db_path)))
1385 		{
1386 			DPRINTF(E_ERROR, L_HTTP, "Rejecting wide link %s -> %s\n",
1387 						orig_path, path);
1388 			return -403;
1389 		}
1390 	}
1391 	else
1392 		path = orig_path;
1393 
1394 	fd = open(path, O_RDONLY);
1395 	if (fd < 0)
1396 		DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
1397 
1398 	return fd;
1399 }
1400 
1401 static void
SendResp_icon(struct upnphttp * h,char * icon)1402 SendResp_icon(struct upnphttp * h, char * icon)
1403 {
1404 	char header[512];
1405 	char mime[12] = "image/";
1406 	char *data;
1407 	int size;
1408 	struct string_s str;
1409 
1410 	if( strcmp(icon, "sm.png") == 0 )
1411 	{
1412 		DPRINTF(E_DEBUG, L_HTTP, "Sending small PNG icon\n");
1413 		data = (char *)png_sm;
1414 		size = sizeof(png_sm)-1;
1415 		strcpy(mime+6, "png");
1416 	}
1417 	else if( strcmp(icon, "lrg.png") == 0 )
1418 	{
1419 		DPRINTF(E_DEBUG, L_HTTP, "Sending large PNG icon\n");
1420 		data = (char *)png_lrg;
1421 		size = sizeof(png_lrg)-1;
1422 		strcpy(mime+6, "png");
1423 	}
1424 	else if( strcmp(icon, "sm.jpg") == 0 )
1425 	{
1426 		DPRINTF(E_DEBUG, L_HTTP, "Sending small JPEG icon\n");
1427 		data = (char *)jpeg_sm;
1428 		size = sizeof(jpeg_sm)-1;
1429 		strcpy(mime+6, "jpeg");
1430 	}
1431 	else if( strcmp(icon, "lrg.jpg") == 0 )
1432 	{
1433 		DPRINTF(E_DEBUG, L_HTTP, "Sending large JPEG icon\n");
1434 		data = (char *)jpeg_lrg;
1435 		size = sizeof(jpeg_lrg)-1;
1436 		strcpy(mime+6, "jpeg");
1437 	}
1438 	else
1439 	{
1440 		DPRINTF(E_WARN, L_HTTP, "Invalid icon request: %s\n", icon);
1441 		Send404(h);
1442 		return;
1443 	}
1444 
1445 	INIT_STR(str, header);
1446 
1447 	start_dlna_header(&str, 200, "Interactive", mime);
1448 	strcatf(&str, "Content-Length: %d\r\n\r\n", size);
1449 
1450 	if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1451 	{
1452 		if( h->req_command != EHead )
1453 			send_data(h, data, size, 0);
1454 	}
1455 	CloseSocket_upnphttp(h);
1456 }
1457 
1458 static void
SendResp_albumArt(struct upnphttp * h,char * object)1459 SendResp_albumArt(struct upnphttp * h, char * object)
1460 {
1461 	char header[512];
1462 	char *path;
1463 	off_t size;
1464 	long long id;
1465 	int fd;
1466 	struct string_s str;
1467 
1468 	if( h->reqflags & (FLAG_XFERSTREAMING|FLAG_RANGE) )
1469 	{
1470 		DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
1471 		Send406(h);
1472 		return;
1473 	}
1474 
1475 	id = strtoll(object, NULL, 10);
1476 
1477 	path = sql_get_text_field(db, "SELECT PATH from ALBUM_ART where ID = '%lld'", id);
1478 	if( !path )
1479 	{
1480 		DPRINTF(E_WARN, L_HTTP, "ALBUM_ART ID %s not found, responding ERROR 404\n", object);
1481 		Send404(h);
1482 		return;
1483 	}
1484 	DPRINTF(E_INFO, L_HTTP, "Serving album art ID: %lld [%s]\n", id, path);
1485 
1486 	fd = _open_file(path);
1487 	if( fd < 0 ) {
1488 		sqlite3_free(path);
1489 		if (fd == -403)
1490 			Send403(h);
1491 		else
1492 			Send404(h);
1493 		return;
1494 	}
1495 	sqlite3_free(path);
1496 	size = lseek(fd, 0, SEEK_END);
1497 	lseek(fd, 0, SEEK_SET);
1498 
1499 	INIT_STR(str, header);
1500 
1501 	start_dlna_header(&str, 200, "Interactive", "image/jpeg");
1502 	strcatf(&str, "Content-Length: %jd\r\n"
1503 	              "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n\r\n",
1504 	              (intmax_t)size);
1505 
1506 	if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1507 	{
1508 		if( h->req_command != EHead )
1509 			send_file(h, fd, 0, size-1);
1510 	}
1511 	close(fd);
1512 	CloseSocket_upnphttp(h);
1513 }
1514 
1515 static void
SendResp_caption(struct upnphttp * h,char * object)1516 SendResp_caption(struct upnphttp * h, char * object)
1517 {
1518 	char header[512];
1519 	char *path;
1520 	off_t size;
1521 	long long id;
1522 	int fd;
1523 	struct string_s str;
1524 
1525 	id = strtoll(object, NULL, 10);
1526 
1527 	path = sql_get_text_field(db, "SELECT PATH from CAPTIONS where ID = %lld", id);
1528 	if( !path )
1529 	{
1530 		DPRINTF(E_WARN, L_HTTP, "CAPTION ID %s not found, responding ERROR 404\n", object);
1531 		Send404(h);
1532 		return;
1533 	}
1534 	DPRINTF(E_INFO, L_HTTP, "Serving caption ID: %lld [%s]\n", id, path);
1535 
1536 	fd = _open_file(path);
1537 	if( fd < 0 ) {
1538 		sqlite3_free(path);
1539 		if (fd == -403)
1540 			Send403(h);
1541 		else
1542 			Send404(h);
1543 		return;
1544 	}
1545 	sqlite3_free(path);
1546 	size = lseek(fd, 0, SEEK_END);
1547 	lseek(fd, 0, SEEK_SET);
1548 
1549 	INIT_STR(str, header);
1550 
1551 	start_dlna_header(&str, 200, "Interactive", "smi/caption");
1552 	strcatf(&str, "Content-Length: %jd\r\n\r\n", (intmax_t)size);
1553 
1554 	if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1555 	{
1556 		if( h->req_command != EHead )
1557 			send_file(h, fd, 0, size-1);
1558 	}
1559 	close(fd);
1560 	CloseSocket_upnphttp(h);
1561 }
1562 
1563 static void
SendResp_thumbnail(struct upnphttp * h,char * object)1564 SendResp_thumbnail(struct upnphttp * h, char * object)
1565 {
1566 	char header[512];
1567 	char *path;
1568 	long long id;
1569 	ExifData *ed;
1570 	ExifLoader *l;
1571 	struct string_s str;
1572 
1573 	if( h->reqflags & (FLAG_XFERSTREAMING|FLAG_RANGE) )
1574 	{
1575 		DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
1576 		Send406(h);
1577 		return;
1578 	}
1579 
1580 	id = strtoll(object, NULL, 10);
1581 	path = sql_get_text_field(db, "SELECT PATH from DETAILS where ID = '%lld'", id);
1582 	if( !path )
1583 	{
1584 		DPRINTF(E_WARN, L_HTTP, "DETAIL ID %s not found, responding ERROR 404\n", object);
1585 		Send404(h);
1586 		return;
1587 	}
1588 	DPRINTF(E_INFO, L_HTTP, "Serving thumbnail for ObjectId: %lld [%s]\n", id, path);
1589 
1590 	if( access(path, F_OK) != 0 )
1591 	{
1592 		DPRINTF(E_ERROR, L_HTTP, "Error accessing %s\n", path);
1593 		Send404(h);
1594 		sqlite3_free(path);
1595 		return;
1596 	}
1597 
1598 	l = exif_loader_new();
1599 	exif_loader_write_file(l, path);
1600 	ed = exif_loader_get_data(l);
1601 	exif_loader_unref(l);
1602 	sqlite3_free(path);
1603 
1604 	if( !ed || !ed->size )
1605 	{
1606 		Send404(h);
1607 		if( ed )
1608 			exif_data_unref(ed);
1609 		return;
1610 	}
1611 
1612 	INIT_STR(str, header);
1613 
1614 	start_dlna_header(&str, 200, "Interactive", "image/jpeg");
1615 	strcatf(&str, "Content-Length: %jd\r\n"
1616 	              "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN;DLNA.ORG_CI=1\r\n\r\n",
1617 	              (intmax_t)ed->size);
1618 
1619 	if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1620 	{
1621 		if( h->req_command != EHead )
1622 			send_data(h, (char *)ed->data, ed->size, 0);
1623 	}
1624 	exif_data_unref(ed);
1625 	CloseSocket_upnphttp(h);
1626 }
1627 
1628 static void
SendResp_resizedimg(struct upnphttp * h,char * object)1629 SendResp_resizedimg(struct upnphttp * h, char * object)
1630 {
1631 	char header[512];
1632 	char buf[128];
1633 	struct string_s str;
1634 	char **result;
1635 	char dlna_pn[22];
1636 	uint32_t dlna_flags = DLNA_FLAG_DLNA_V1_5|DLNA_FLAG_HTTP_STALLING|DLNA_FLAG_TM_B|DLNA_FLAG_TM_I;
1637 	int width=640, height=480, dstw, dsth, size;
1638 	int srcw, srch;
1639 	unsigned char * data = NULL;
1640 	char *path, *file_path = NULL;
1641 	char *resolution = NULL;
1642 	char *key, *val;
1643 	char *saveptr, *item = NULL;
1644 	int rotate = 0;
1645 	int pixw = 0, pixh = 0;
1646 	long long id;
1647 	int rows=0, chunked, ret;
1648 	image_s *imsrc = NULL, *imdst = NULL;
1649 	int scale = 1;
1650 	const char *tmode;
1651 
1652 	id = strtoll(object, &saveptr, 10);
1653 	snprintf(buf, sizeof(buf), "SELECT PATH, RESOLUTION, ROTATION from DETAILS where ID = '%lld'", (long long)id);
1654 	ret = sql_get_table(db, buf, &result, &rows, NULL);
1655 	if( ret != SQLITE_OK )
1656 	{
1657 		Send500(h);
1658 		return;
1659 	}
1660 	if( rows )
1661 	{
1662 		file_path = result[3];
1663 		resolution = result[4];
1664                 if (result[5])
1665 			rotate = atoi(result[5]);
1666 	}
1667 	if( !file_path || !resolution || (access(file_path, F_OK) != 0) )
1668 	{
1669 		DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
1670 		sqlite3_free_table(result);
1671 		Send404(h);
1672 		return;
1673 	}
1674 
1675 	if( saveptr )
1676 		saveptr = strchr(saveptr, '?');
1677 	path = saveptr ? saveptr + 1 : object;
1678 	for( item = strtok_r(path, "&,", &saveptr); item != NULL; item = strtok_r(NULL, "&,", &saveptr) )
1679 	{
1680 		decodeString(item, 1);
1681 		val = item;
1682 		key = strsep(&val, "=");
1683 		if( !val )
1684 			continue;
1685 		DPRINTF(E_DEBUG, L_GENERAL, "%s: %s\n", key, val);
1686 		if( strcasecmp(key, "width") == 0 )
1687 		{
1688 			width = atoi(val);
1689 		}
1690 		else if( strcasecmp(key, "height") == 0 )
1691 		{
1692 			height = atoi(val);
1693 		}
1694 		else if( strcasecmp(key, "rotation") == 0 )
1695 		{
1696 			rotate = (rotate + atoi(val)) % 360;
1697 			sql_exec(db, "UPDATE DETAILS set ROTATION = %d where ID = %lld", rotate, id);
1698 		}
1699 		else if( strcasecmp(key, "pixelshape") == 0 )
1700 		{
1701 			ret = sscanf(val, "%d:%d", &pixw, &pixh);
1702 			if( ret != 2 )
1703 				pixw = pixh = 0;
1704 		}
1705 	}
1706 
1707 #if USE_FORK
1708 	pid_t newpid = 0;
1709 	newpid = process_fork(h->req_client);
1710 	if( newpid > 0 )
1711 	{
1712 		CloseSocket_upnphttp(h);
1713 		goto resized_error;
1714 	}
1715 #endif
1716 	if( h->reqflags & (FLAG_XFERSTREAMING|FLAG_RANGE) )
1717 	{
1718 		DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
1719 		Send406(h);
1720 		goto resized_error;
1721 	}
1722 
1723 	DPRINTF(E_INFO, L_HTTP, "Serving resized image for ObjectId: %lld [%s]\n", id, file_path);
1724 	if( rotate )
1725 		DPRINTF(E_DEBUG, L_HTTP, "Rotating image %d degrees\n", rotate);
1726 	switch( rotate )
1727 	{
1728 		case 90:
1729 			ret = sscanf(resolution, "%dx%d", &srch, &srcw);
1730 			rotate = ROTATE_90;
1731 			break;
1732 		case 270:
1733 			ret = sscanf(resolution, "%dx%d", &srch, &srcw);
1734 			rotate = ROTATE_270;
1735 			break;
1736 		case 180:
1737 			ret = sscanf(resolution, "%dx%d", &srcw, &srch);
1738 			rotate = ROTATE_180;
1739 			break;
1740 		default:
1741 			ret = sscanf(resolution, "%dx%d", &srcw, &srch);
1742 			rotate = ROTATE_NONE;
1743 			break;
1744 	}
1745 	if( ret != 2 )
1746 	{
1747 		Send500(h);
1748 		return;
1749 	}
1750 	/* Figure out the best destination resolution we can use */
1751 	dstw = width;
1752 	dsth = ((((width<<10)/srcw)*srch)>>10);
1753 	if( dsth > height )
1754 	{
1755 		dsth = height;
1756 		dstw = (((height<<10)/srch) * srcw>>10);
1757 	}
1758 	/* Account for pixel shape */
1759 	if( pixw && pixh )
1760 	{
1761 		if( pixh > pixw )
1762 			dsth = dsth * pixw / pixh;
1763 		else if( pixw > pixh )
1764 			dstw = dstw * pixh / pixw;
1765 	}
1766 
1767 	if( dstw <= 160 && dsth <= 160 )
1768 		strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_TN;");
1769 	else if( dstw <= 640 && dsth <= 480 )
1770 		strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_SM;");
1771 	else if( dstw <= 1024 && dsth <= 768 )
1772 		strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_MED;");
1773 	else
1774 		strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_LRG;");
1775 
1776 	if( srcw>>4 >= dstw && srch>>4 >= dsth)
1777 		scale = 8;
1778 	else if( srcw>>3 >= dstw && srch>>3 >= dsth )
1779 		scale = 4;
1780 	else if( srcw>>2 >= dstw && srch>>2 >= dsth )
1781 		scale = 2;
1782 
1783 	INIT_STR(str, header);
1784 
1785 #if USE_FORK
1786 	if( (h->reqflags & FLAG_XFERBACKGROUND) && (setpriority(PRIO_PROCESS, 0, 19) == 0) )
1787 		tmode = "Background";
1788 	else
1789 #endif
1790 		tmode = "Interactive";
1791 	start_dlna_header(&str, 200, tmode, "image/jpeg");
1792 	strcatf(&str, "contentFeatures.dlna.org: %sDLNA.ORG_CI=1;DLNA.ORG_FLAGS=%08X%024X\r\n",
1793 	              dlna_pn, dlna_flags, 0);
1794 
1795 	if( strcmp(h->HttpVer, "HTTP/1.0") == 0 )
1796 	{
1797 		chunked = 0;
1798 		imsrc = image_new_from_jpeg(file_path, 1, NULL, 0, scale, rotate);
1799 	}
1800 	else
1801 	{
1802 		chunked = 1;
1803 		strcatf(&str, "Transfer-Encoding: chunked\r\n\r\n");
1804 	}
1805 
1806 	if( !chunked )
1807 	{
1808 		if( !imsrc )
1809 		{
1810 			DPRINTF(E_WARN, L_HTTP, "Unable to open image %s!\n", file_path);
1811 			Send500(h);
1812 			goto resized_error;
1813 		}
1814 
1815 		imdst = image_resize(imsrc, dstw, dsth);
1816 		data = image_save_to_jpeg_buf(imdst, &size);
1817 
1818 		strcatf(&str, "Content-Length: %d\r\n\r\n", size);
1819 	}
1820 
1821 	if( (send_data(h, str.data, str.off, 0) == 0) && (h->req_command != EHead) )
1822 	{
1823 		if( chunked )
1824 		{
1825 			imsrc = image_new_from_jpeg(file_path, 1, NULL, 0, scale, rotate);
1826 			if( !imsrc )
1827 			{
1828 				DPRINTF(E_WARN, L_HTTP, "Unable to open image %s!\n", file_path);
1829 				Send500(h);
1830 				goto resized_error;
1831 			}
1832 			imdst = image_resize(imsrc, dstw, dsth);
1833 			data = image_save_to_jpeg_buf(imdst, &size);
1834 
1835 			ret = sprintf(buf, "%x\r\n", size);
1836 			send_data(h, buf, ret, MSG_MORE);
1837 			send_data(h, (char *)data, size, MSG_MORE);
1838 			send_data(h, "\r\n0\r\n\r\n", 7, 0);
1839 		}
1840 		else
1841 		{
1842 			send_data(h, (char *)data, size, 0);
1843 		}
1844 	}
1845 	DPRINTF(E_INFO, L_HTTP, "Done serving %s\n", file_path);
1846 	if( imsrc )
1847 		image_free(imsrc);
1848 	if( imdst )
1849 		image_free(imdst);
1850 	CloseSocket_upnphttp(h);
1851 resized_error:
1852 	sqlite3_free_table(result);
1853 #if USE_FORK
1854 	if( newpid == 0 )
1855 		_exit(0);
1856 #endif
1857 }
1858 
1859 static void
SendResp_dlnafile(struct upnphttp * h,char * object)1860 SendResp_dlnafile(struct upnphttp *h, char *object)
1861 {
1862 	char header[1024];
1863 	struct string_s str;
1864 	char buf[128];
1865 	char **result;
1866 	int rows, ret;
1867 	off_t total, offset, size;
1868 	int64_t id;
1869 	int sendfh;
1870 	uint32_t dlna_flags = DLNA_FLAG_DLNA_V1_5|DLNA_FLAG_HTTP_STALLING|DLNA_FLAG_TM_B;
1871 	uint32_t cflags = h->req_client ? h->req_client->type->flags : 0;
1872 	const char *tmode;
1873 	enum client_types ctype = h->req_client ? h->req_client->type->type : 0;
1874 	static struct { int64_t id;
1875 	                enum client_types client;
1876 	                char path[PATH_MAX];
1877 	                char mime[32];
1878 	                char dlna[96];
1879 	              } last_file = { 0, 0 };
1880 #if USE_FORK
1881 	pid_t newpid = 0;
1882 #endif
1883 
1884 	id = strtoll(object, NULL, 10);
1885 	if( cflags & FLAG_MS_PFS )
1886 	{
1887 		if( strstr(object, "?albumArt=true") )
1888 		{
1889 			char *art;
1890 			art = sql_get_text_field(db, "SELECT ALBUM_ART from DETAILS where ID = '%lld'", id);
1891 			if (art)
1892 			{
1893 				SendResp_albumArt(h, art);
1894 				sqlite3_free(art);
1895 			}
1896 			else
1897 				Send404(h);
1898 			return;
1899 		}
1900 	}
1901 	if( id != last_file.id || ctype != last_file.client )
1902 	{
1903 		snprintf(buf, sizeof(buf), "SELECT PATH, MIME, DLNA_PN from DETAILS where ID = '%lld'", (long long)id);
1904 		ret = sql_get_table(db, buf, &result, &rows, NULL);
1905 		if( (ret != SQLITE_OK) )
1906 		{
1907 			DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %lld!\n", (long long)id);
1908 			Send500(h);
1909 			return;
1910 		}
1911 		if( !rows || !result[3] || !result[4] )
1912 		{
1913 			DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
1914 			sqlite3_free_table(result);
1915 			Send404(h);
1916 			return;
1917 		}
1918 		/* Cache the result */
1919 		last_file.id = id;
1920 		last_file.client = ctype;
1921 		strncpy(last_file.path, result[3], sizeof(last_file.path)-1);
1922 		if( result[4] )
1923 		{
1924 			strncpy(last_file.mime, result[4], sizeof(last_file.mime)-1);
1925 			/* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */
1926 			if( cflags & FLAG_SAMSUNG )
1927 			{
1928 				if( strcmp(last_file.mime+6, "x-matroska") == 0 )
1929 					strcpy(last_file.mime+8, "mkv");
1930 				/* Samsung TV's such as the A750 can natively support many
1931 				   Xvid/DivX AVI's however, the DLNA server needs the
1932 				   mime type to say video/mpeg */
1933 				else if( ctype == ESamsungSeriesA && strcmp(last_file.mime+6, "x-msvideo") == 0 )
1934 					strcpy(last_file.mime+6, "mpeg");
1935 			}
1936 			/* ... and Sony BDP-S370 won't play MKV unless we pretend it's a DiVX file */
1937 			else if( ctype == ESonyBDP )
1938 			{
1939 				if( strcmp(last_file.mime+6, "x-matroska") == 0 ||
1940 				    strcmp(last_file.mime+6, "mpeg") == 0 )
1941 					strcpy(last_file.mime+6, "divx");
1942 			}
1943 		}
1944 		if( result[5] )
1945 			snprintf(last_file.dlna, sizeof(last_file.dlna), "DLNA.ORG_PN=%s;", result[5]);
1946 		else
1947 			last_file.dlna[0] = '\0';
1948 		sqlite3_free_table(result);
1949 	}
1950 #if USE_FORK
1951 	newpid = process_fork(h->req_client);
1952 	if( newpid > 0 )
1953 	{
1954 		CloseSocket_upnphttp(h);
1955 		return;
1956 	}
1957 #endif
1958 
1959 	DPRINTF(E_INFO, L_HTTP, "Serving DetailID: %lld [%s]\n", (long long)id, last_file.path);
1960 
1961 	if( h->reqflags & FLAG_XFERSTREAMING )
1962 	{
1963 		if( strncmp(last_file.mime, "image", 5) == 0 )
1964 		{
1965 			DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
1966 			Send406(h);
1967 			goto error;
1968 		}
1969 	}
1970 	else if( h->reqflags & FLAG_XFERINTERACTIVE )
1971 	{
1972 		if( h->reqflags & FLAG_REALTIMEINFO )
1973 		{
1974 			DPRINTF(E_WARN, L_HTTP, "Bad realTimeInfo flag with Interactive request!\n");
1975 			Send400(h);
1976 			goto error;
1977 		}
1978 		if( strncmp(last_file.mime, "image", 5) != 0 )
1979 		{
1980 			DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Interactive without an image!\n");
1981 			/* Samsung TVs (well, at least the A950) do this for some reason,
1982 			 * and I don't see them fixing this bug any time soon. */
1983 			if( !(cflags & FLAG_SAMSUNG) || GETFLAG(DLNA_STRICT_MASK) )
1984 			{
1985 				Send406(h);
1986 				goto error;
1987 			}
1988 		}
1989 	}
1990 
1991 	offset = h->req_RangeStart;
1992 	sendfh = _open_file(last_file.path);
1993 	if( sendfh < 0 ) {
1994 		if (sendfh == -403)
1995 			Send403(h);
1996 		else
1997 			Send404(h);
1998 		goto error;
1999 	}
2000 	size = lseek(sendfh, 0, SEEK_END);
2001 	lseek(sendfh, 0, SEEK_SET);
2002 
2003 	INIT_STR(str, header);
2004 
2005 #if USE_FORK
2006 	if( (h->reqflags & FLAG_XFERBACKGROUND) && (setpriority(PRIO_PROCESS, 0, 19) == 0) )
2007 		tmode = "Background";
2008 	else
2009 #endif
2010 	if( strncmp(last_file.mime, "image", 5) == 0 )
2011 		tmode = "Interactive";
2012 	else
2013 		tmode = "Streaming";
2014 
2015 	start_dlna_header(&str, (h->reqflags & FLAG_RANGE ? 206 : 200), tmode, last_file.mime);
2016 
2017 	if( h->reqflags & FLAG_RANGE )
2018 	{
2019 		if( !h->req_RangeEnd || h->req_RangeEnd == size )
2020 		{
2021 			h->req_RangeEnd = size - 1;
2022 		}
2023 		if( (h->req_RangeStart > h->req_RangeEnd) || (h->req_RangeStart < 0) )
2024 		{
2025 			DPRINTF(E_WARN, L_HTTP, "Specified range was invalid!\n");
2026 			Send400(h);
2027 			close(sendfh);
2028 			goto error;
2029 		}
2030 		if( h->req_RangeEnd >= size )
2031 		{
2032 			DPRINTF(E_WARN, L_HTTP, "Specified range was outside file boundaries!\n");
2033 			Send416(h);
2034 			close(sendfh);
2035 			goto error;
2036 		}
2037 
2038 		total = h->req_RangeEnd - h->req_RangeStart + 1;
2039 		strcatf(&str, "Content-Length: %jd\r\n"
2040 		              "Content-Range: bytes %jd-%jd/%jd\r\n",
2041 		              (intmax_t)total, (intmax_t)h->req_RangeStart,
2042 		              (intmax_t)h->req_RangeEnd, (intmax_t)size);
2043 	}
2044 	else
2045 	{
2046 		h->req_RangeEnd = size - 1;
2047 		total = size;
2048 		strcatf(&str, "Content-Length: %jd\r\n", (intmax_t)total);
2049 	}
2050 
2051 	switch( *last_file.mime )
2052 	{
2053 		case 'i':
2054 			dlna_flags |= DLNA_FLAG_TM_I;
2055 			break;
2056 		case 'a':
2057 		case 'v':
2058 		default:
2059 			dlna_flags |= DLNA_FLAG_TM_S;
2060 			break;
2061 	}
2062 
2063 	if( h->reqflags & FLAG_CAPTION )
2064 	{
2065 		if( sql_get_int_field(db, "SELECT ID from CAPTIONS where ID = '%lld'", (long long)id) > 0 )
2066 			strcatf(&str, "CaptionInfo.sec: http://%s:%d/Captions/%lld.srt\r\n",
2067 			              lan_addr[h->iface].str, runtime_vars.port, (long long)id);
2068 	}
2069 
2070 	strcatf(&str, "Accept-Ranges: bytes\r\n"
2071 	              "contentFeatures.dlna.org: %sDLNA.ORG_OP=%02X;DLNA.ORG_CI=%X;DLNA.ORG_FLAGS=%08X%024X\r\n\r\n",
2072 	              last_file.dlna, 1, 0, dlna_flags, 0);
2073 
2074 	//DEBUG DPRINTF(E_DEBUG, L_HTTP, "RESPONSE: %s\n", str.data);
2075 	if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
2076 	{
2077 		if( h->req_command != EHead )
2078 			send_file(h, sendfh, offset, h->req_RangeEnd);
2079 	}
2080 	close(sendfh);
2081 
2082 	CloseSocket_upnphttp(h);
2083 error:
2084 #if USE_FORK
2085 	if( newpid == 0 )
2086 		_exit(0);
2087 #endif
2088 	return;
2089 }
2090