Wrap up version 1.3.3.
[minidlna.git] / upnphttp.c
blob477ebef4caee2e58c89a401b8d9b3cc0c4c704d7
1 /* MiniDLNA project
3 * http://sourceforge.net/projects/minidlna/
5 * MiniDLNA media server
6 * Copyright (C) 2008-2009 Justin Maggard
8 * This file is part of MiniDLNA.
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.
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.
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/>.
22 * Portions of the code from the MiniUPnP project:
24 * Copyright (c) 2006-2007, Thomas Bernard
25 * All rights reserved.
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.
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.
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>
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"
86 #define MAX_BUFFER_SIZE 2147483647
87 #define MIN_BUFFER_SIZE 65536
89 #define INIT_STR(s, d) { s.data = d; s.size = sizeof(d); s.off = 0; }
91 #include "icons.c"
93 enum event_type {
94 E_INVALID,
95 E_SUBSCRIBE,
96 E_RENEW
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);
107 struct upnphttp *
108 New_upnphttp(int s)
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;
122 void
123 CloseSocket_upnphttp(struct upnphttp * h)
126 event_module.del(&h->ev, EV_FLAG_CLOSING);
127 if(close(h->ev.fd) < 0)
129 DPRINTF(E_ERROR, L_HTTP, "CloseSocket_upnphttp: close(%d): %s\n", h->ev.fd, strerror(errno));
131 h->ev.fd = -1;
132 h->state = 100;
135 void
136 Delete_upnphttp(struct upnphttp * h)
138 if(h)
140 if(h->ev.fd >= 0)
141 CloseSocket_upnphttp(h);
142 free(h->req_buf);
143 free(h->res_buf);
144 free(h);
148 /* parse HttpHeaders of the REQUEST */
149 static void
150 ParseHttpHeaders(struct upnphttp * h)
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))
161 colon = strchr(line, ':');
162 if(colon)
164 if(strncasecmp(line, "Content-Length", 14)==0)
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;
175 else if(strncasecmp(line, "SOAPAction", 10)==0)
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] == '\'')))
187 p++;
188 n -= 2;
190 h->req_soapAction = p;
191 h->req_soapActionLen = n;
193 else if(strncasecmp(line, "Callback", 8)==0)
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);
204 else if(strncasecmp(line, "SID", 3)==0)
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++)
210 if(!isspace(*p))
212 p = NULL; //unexpected header
213 break;
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;
227 else if(strncasecmp(line, "NT", 2)==0)
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;
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)
247 p = colon + 1;
248 while(isspace(*p))
249 p++;
250 if(strncasecmp(p, "Second-", 7)==0) {
251 h->req_Timeout = atoi(p+7);
254 // Range: bytes=xxx-yyy
255 else if(strncasecmp(line, "Range", 5)==0)
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);
269 else if(strncasecmp(line, "Host", 4)==0)
271 int i;
272 h->reqflags |= FLAG_HOST;
273 p = colon + 1;
274 while(isspace(*p))
275 p++;
276 n = 0;
277 while(p[n] >= ' ')
278 n++;
279 h->req_Host = p;
280 h->req_HostLen = n;
281 for(n = 0; n < n_lan_addr; n++)
283 for(i = 0; lan_addr[n].str[i]; i++)
285 if(lan_addr[n].str[i] != p[i])
286 break;
288 if(i && !lan_addr[n].str[i])
290 h->iface = n;
291 break;
295 else if(strncasecmp(line, "User-Agent", 10)==0)
297 int i;
298 /* Skip client detection if we already detected it. */
299 if( client )
300 goto next_header;
301 p = colon + 1;
302 while(isspace(*p))
303 p++;
304 for (i = 0; client_types[i].name; i++)
306 if (client_types[i].match_type != EUserAgent)
307 continue;
308 if (strstrc(p, client_types[i].match, '\r') != NULL)
310 client = i;
311 break;
315 else if(strncasecmp(line, "X-AV-Client-Info", 16)==0)
317 int i;
318 /* Skip client detection if we already detected it. */
319 if( client && client_types[client].type < EStandardDLNA150 )
320 goto next_header;
321 p = colon + 1;
322 while(isspace(*p))
323 p++;
324 for (i = 0; client_types[i].name; i++)
326 if (client_types[i].match_type != EXAVClientInfo)
327 continue;
328 if (strstrc(p, client_types[i].match, '\r') != NULL)
330 client = i;
331 break;
335 else if(strncasecmp(line, "Transfer-Encoding", 17)==0)
337 p = colon + 1;
338 while(isspace(*p))
339 p++;
340 if(strncasecmp(p, "chunked", 7)==0)
342 h->reqflags |= FLAG_CHUNKED;
345 else if(strncasecmp(line, "Accept-Language", 15)==0)
347 h->reqflags |= FLAG_LANGUAGE;
349 else if(strncasecmp(line, "getcontentFeatures.dlna.org", 27)==0)
351 p = colon + 1;
352 while(isspace(*p))
353 p++;
354 if( (*p != '1') || !isspace(p[1]) )
355 h->reqflags |= FLAG_INVALID_REQ;
357 else if(strncasecmp(line, "TimeSeekRange.dlna.org", 22)==0)
359 h->reqflags |= FLAG_TIMESEEK;
361 else if(strncasecmp(line, "PlaySpeed.dlna.org", 18)==0)
363 h->reqflags |= FLAG_PLAYSPEED;
365 else if(strncasecmp(line, "realTimeInfo.dlna.org", 21)==0)
367 h->reqflags |= FLAG_REALTIMEINFO;
369 else if(strncasecmp(line, "getAvailableSeekRange.dlna.org", 21)==0)
371 p = colon + 1;
372 while(isspace(*p))
373 p++;
374 if( (*p != '1') || !isspace(p[1]) )
375 h->reqflags |= FLAG_INVALID_REQ;
377 else if(strncasecmp(line, "transferMode.dlna.org", 21)==0)
379 p = colon + 1;
380 while(isspace(*p))
381 p++;
382 if(strncasecmp(p, "Streaming", 9)==0)
384 h->reqflags |= FLAG_XFERSTREAMING;
386 if(strncasecmp(p, "Interactive", 11)==0)
388 h->reqflags |= FLAG_XFERINTERACTIVE;
390 if(strncasecmp(p, "Background", 10)==0)
392 h->reqflags |= FLAG_XFERBACKGROUND;
395 else if(strncasecmp(line, "getCaptionInfo.sec", 18)==0)
397 h->reqflags |= FLAG_CAPTION;
399 else if(strncasecmp(line, "FriendlyName", 12)==0)
401 int i;
402 p = colon + 1;
403 while(isspace(*p))
404 p++;
405 for (i = 0; client_types[i].name; i++)
407 if (client_types[i].match_type != EFriendlyName)
408 continue;
409 if (strstrc(p, client_types[i].match, '\r') != NULL)
411 client = i;
412 break;
416 else if(strncasecmp(line, "uctt.upnp.org:", 14)==0)
418 /* Conformance testing */
419 SETFLAG(DLNA_STRICT_MASK);
422 next_header:
423 line = strstr(line, "\r\n");
424 if (!line)
425 return;
426 line += 2;
428 if (h->reqflags & FLAG_CHUNKED)
430 char *endptr;
431 h->req_chunklen = -1;
432 if (h->req_buflen <= h->req_contentoff)
433 return;
434 while( (line < (h->req_buf + h->req_buflen)) &&
435 ((h->req_chunklen = strtol(line, &endptr, 16)) > 0) &&
436 (endptr != line) )
438 endptr = strstr(endptr, "\r\n");
439 if (!endptr)
441 return;
443 line = endptr+h->req_chunklen+2;
446 if( endptr == line )
448 h->req_chunklen = -1;
449 return;
452 /* If the client type wasn't found, search the cache.
453 * This is done because a lot of clients like to send a
454 * different User-Agent with different types of requests. */
455 h->req_client = SearchClientCache(h->clientaddr, 0);
456 /* Add this client to the cache if it's not there already. */
457 if (!h->req_client)
459 h->req_client = AddClientCache(h->clientaddr, client);
461 else if (client)
463 enum client_types type = client_types[client].type;
464 enum client_types ctype = h->req_client->type->type;
465 /* If we know the client and our new detection is generic, use our cached info */
466 /* If we detected a Samsung Series B earlier, don't overwrite it with Series A info */
467 if ((ctype && ctype < EStandardDLNA150 && type >= EStandardDLNA150) ||
468 (ctype == ESamsungSeriesB && type == ESamsungSeriesA))
469 return;
470 h->req_client->type = &client_types[client];
471 h->req_client->age = time(NULL);
475 /* very minimalistic 400 error message */
476 static void
477 Send400(struct upnphttp * h)
479 static const char body400[] =
480 "<HTML><HEAD><TITLE>400 Bad Request</TITLE></HEAD>"
481 "<BODY><H1>Bad Request</H1>The request is invalid"
482 " for this HTTP version.</BODY></HTML>\r\n";
483 h->respflags = FLAG_HTML;
484 BuildResp2_upnphttp(h, 400, "Bad Request",
485 body400, sizeof(body400) - 1);
486 SendResp_upnphttp(h);
487 CloseSocket_upnphttp(h);
490 /* very minimalistic 403 error message */
491 static void
492 Send403(struct upnphttp * h)
494 static const char body403[] =
495 "<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>"
496 "<BODY><H1>Forbidden</H1>You don't have permission to access this resource."
497 "</BODY></HTML>\r\n";
498 h->respflags = FLAG_HTML;
499 BuildResp2_upnphttp(h, 403, "Forbidden",
500 body403, sizeof(body403) - 1);
501 SendResp_upnphttp(h);
502 CloseSocket_upnphttp(h);
505 /* very minimalistic 404 error message */
506 static void
507 Send404(struct upnphttp * h)
509 static const char body404[] =
510 "<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
511 "<BODY><H1>Not Found</H1>The requested URL was not found"
512 " on this server.</BODY></HTML>\r\n";
513 h->respflags = FLAG_HTML;
514 BuildResp2_upnphttp(h, 404, "Not Found",
515 body404, sizeof(body404) - 1);
516 SendResp_upnphttp(h);
517 CloseSocket_upnphttp(h);
520 /* very minimalistic 406 error message */
521 static void
522 Send406(struct upnphttp * h)
524 static const char body406[] =
525 "<HTML><HEAD><TITLE>406 Not Acceptable</TITLE></HEAD>"
526 "<BODY><H1>Not Acceptable</H1>An unsupported operation"
527 " was requested.</BODY></HTML>\r\n";
528 h->respflags = FLAG_HTML;
529 BuildResp2_upnphttp(h, 406, "Not Acceptable",
530 body406, sizeof(body406) - 1);
531 SendResp_upnphttp(h);
532 CloseSocket_upnphttp(h);
535 /* very minimalistic 416 error message */
536 static void
537 Send416(struct upnphttp * h)
539 static const char body416[] =
540 "<HTML><HEAD><TITLE>416 Requested Range Not Satisfiable</TITLE></HEAD>"
541 "<BODY><H1>Requested Range Not Satisfiable</H1>The requested range"
542 " was outside the file's size.</BODY></HTML>\r\n";
543 h->respflags = FLAG_HTML;
544 BuildResp2_upnphttp(h, 416, "Requested Range Not Satisfiable",
545 body416, sizeof(body416) - 1);
546 SendResp_upnphttp(h);
547 CloseSocket_upnphttp(h);
550 /* very minimalistic 500 error message */
551 void
552 Send500(struct upnphttp * h)
554 static const char body500[] =
555 "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>"
556 "<BODY><H1>Internal Server Error</H1>Server encountered "
557 "and Internal Error.</BODY></HTML>\r\n";
558 h->respflags = FLAG_HTML;
559 BuildResp2_upnphttp(h, 500, "Internal Server Errror",
560 body500, sizeof(body500) - 1);
561 SendResp_upnphttp(h);
562 CloseSocket_upnphttp(h);
565 /* very minimalistic 501 error message */
566 void
567 Send501(struct upnphttp * h)
569 static const char body501[] =
570 "<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
571 "<BODY><H1>Not Implemented</H1>The HTTP Method "
572 "is not implemented by this server.</BODY></HTML>\r\n";
573 h->respflags = FLAG_HTML;
574 BuildResp2_upnphttp(h, 501, "Not Implemented",
575 body501, sizeof(body501) - 1);
576 SendResp_upnphttp(h);
577 CloseSocket_upnphttp(h);
580 /* Sends the description generated by the parameter */
581 static void
582 sendXMLdesc(struct upnphttp * h, char * (f)(int *))
584 char * desc;
585 int len;
586 desc = f(&len);
587 if(!desc)
589 DPRINTF(E_ERROR, L_HTTP, "Failed to generate XML description\n");
590 Send500(h);
591 return;
593 BuildResp_upnphttp(h, desc, len);
594 SendResp_upnphttp(h);
595 CloseSocket_upnphttp(h);
596 free(desc);
599 #ifdef READYNAS
600 static void
601 SendResp_readynas_admin(struct upnphttp * h)
603 char body[128];
604 int l;
606 h->respflags = FLAG_HTML;
607 l = snprintf(body, sizeof(body), "<meta http-equiv=\"refresh\" content=\"0; url=https://%s/admin/\">",
608 lan_addr[h->iface].str);
610 BuildResp_upnphttp(h, body, l);
611 SendResp_upnphttp(h);
612 CloseSocket_upnphttp(h);
614 #endif
616 static void
617 SendResp_presentation(struct upnphttp * h)
619 struct string_s str;
620 char body[4096];
621 int a, v, p, i;
623 INIT_STR(str, body);
625 h->respflags = FLAG_HTML;
627 a = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'a*'");
628 v = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'v*'");
629 p = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'i*'");
630 strcatf(&str,
631 "<HTML><HEAD><TITLE>" SERVER_NAME " " MINIDLNA_VERSION "</TITLE><meta http-equiv=\"refresh\" content=\"20\"></HEAD>"
632 "<BODY><div style=\"text-align: center\">"
633 "<h2>" SERVER_NAME " status</h2></div>");
635 strcatf(&str,
636 "<h3>Media library</h3>"
637 "<table border=1 cellpadding=10>"
638 "<tr><td>Audio files</td><td>%d</td></tr>"
639 "<tr><td>Video files</td><td>%d</td></tr>"
640 "<tr><td>Image files</td><td>%d</td></tr>"
641 "</table>", a, v, p);
643 if (GETFLAG(SCANNING_MASK))
644 strcatf(&str,
645 "<br><i>* Media scan in progress</i><br>");
647 strcatf(&str,
648 "<h3>Connected clients</h3>"
649 "<table border=1 cellpadding=10>"
650 "<tr><td>ID</td><td>Type</td><td>IP Address</td><td>HW Address</td><td>Connections</td></tr>");
651 for (i = 0; i < CLIENT_CACHE_SLOTS; i++)
653 if (!clients[i].addr.s_addr)
654 continue;
655 strcatf(&str, "<tr><td>%d</td><td>%s</td><td>%s</td><td>%02X:%02X:%02X:%02X:%02X:%02X</td><td>%d</td></tr>",
656 i, clients[i].type->name, inet_ntoa(clients[i].addr),
657 clients[i].mac[0], clients[i].mac[1], clients[i].mac[2],
658 clients[i].mac[3], clients[i].mac[4], clients[i].mac[5], clients[i].connections);
660 strcatf(&str, "</table>");
662 strcatf(&str, "<br>%d connection%s currently open<br>", number_of_children, (number_of_children == 1 ? "" : "s"));
663 strcatf(&str, "</BODY></HTML>\r\n");
665 BuildResp_upnphttp(h, str.data, str.off);
666 SendResp_upnphttp(h);
667 CloseSocket_upnphttp(h);
670 /* ProcessHTTPPOST_upnphttp()
671 * executes the SOAP query if it is possible */
672 static void
673 ProcessHTTPPOST_upnphttp(struct upnphttp * h)
675 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
677 if(h->req_soapAction)
679 /* we can process the request */
680 DPRINTF(E_DEBUG, L_HTTP, "SOAPAction: %.*s\n", h->req_soapActionLen, h->req_soapAction);
681 ExecuteSoapAction(h,
682 h->req_soapAction,
683 h->req_soapActionLen);
685 else
687 static const char err400str[] =
688 "<html><body>Bad request</body></html>";
689 DPRINTF(E_WARN, L_HTTP, "No SOAPAction in HTTP headers\n");
690 h->respflags = FLAG_HTML;
691 BuildResp2_upnphttp(h, 400, "Bad Request",
692 err400str, sizeof(err400str) - 1);
693 SendResp_upnphttp(h);
694 CloseSocket_upnphttp(h);
697 else
699 /* waiting for remaining data */
700 h->state = 1;
704 static int
705 check_event(struct upnphttp *h)
707 enum event_type type = E_INVALID;
709 if (h->req_Callback)
711 if (h->req_SID || !h->req_NT)
713 BuildResp2_upnphttp(h, 400, "Bad Request",
714 "<html><body>Bad request</body></html>", 37);
716 else if (strncmp(h->req_Callback, "http://", 7) != 0 ||
717 strncmp(h->req_NT, "upnp:event", h->req_NTLen) != 0)
719 /* Missing or invalid CALLBACK : 412 Precondition Failed.
720 * If CALLBACK header is missing or does not contain a valid HTTP URL,
721 * the publisher must respond with HTTP error 412 Precondition Failed*/
722 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
724 else
726 /* Make sure callback URL points to the originating IP */
727 struct in_addr addr;
728 char addrstr[16];
729 int i = 0;
730 const char *p = h->req_Callback + 7;
731 while (!strchr("/:>", *p) && i < sizeof(addrstr) - 1 &&
732 p < (h->req_Callback + h->req_CallbackLen))
734 addrstr[i++] = *(p++);
736 addrstr[i] = '\0';
738 if (inet_pton(AF_INET, addrstr, &addr) <= 0 ||
739 memcmp(&addr, &h->clientaddr, sizeof(struct in_addr)))
741 DPRINTF(E_ERROR, L_HTTP, "Bad callback IP (%s)\n", addrstr);
742 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
744 else
745 type = E_SUBSCRIBE;
748 else if (h->req_SID)
750 /* subscription renew */
751 if (h->req_NT)
753 BuildResp2_upnphttp(h, 400, "Bad Request",
754 "<html><body>Bad request</body></html>", 37);
756 else
757 type = E_RENEW;
759 else
761 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
764 return type;
767 static void
768 ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
770 const char * sid;
771 enum event_type type;
772 DPRINTF(E_DEBUG, L_HTTP, "ProcessHTTPSubscribe %s\n", path);
773 DPRINTF(E_DEBUG, L_HTTP, "Callback '%.*s' Timeout=%d\n",
774 h->req_CallbackLen, h->req_Callback, h->req_Timeout);
775 DPRINTF(E_DEBUG, L_HTTP, "SID '%.*s'\n", h->req_SIDLen, h->req_SID);
777 type = check_event(h);
778 if (type == E_SUBSCRIBE)
780 /* - add to the subscriber list
781 * - respond HTTP/x.x 200 OK
782 * - Send the initial event message */
783 /* Server:, SID:; Timeout: Second-(xx|infinite) */
784 sid = upnpevents_addSubscriber(path, h->req_Callback,
785 h->req_CallbackLen, h->req_Timeout);
786 h->respflags = FLAG_TIMEOUT;
787 if (sid)
789 DPRINTF(E_DEBUG, L_HTTP, "generated sid=%s\n", sid);
790 h->respflags |= FLAG_SID;
791 h->req_SID = sid;
792 h->req_SIDLen = strlen(sid);
794 BuildResp_upnphttp(h, 0, 0);
796 else if (type == E_RENEW)
798 /* subscription renew */
799 if (renewSubscription(h->req_SID, h->req_SIDLen, h->req_Timeout) < 0)
801 /* Invalid SID
802 412 Precondition Failed. If a SID does not correspond to a known,
803 un-expired subscription, the publisher must respond
804 with HTTP error 412 Precondition Failed. */
805 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
807 else
809 /* A DLNA device must enforce a 5 minute timeout */
810 h->respflags = FLAG_TIMEOUT;
811 h->req_Timeout = 300;
812 h->respflags |= FLAG_SID;
813 BuildResp_upnphttp(h, 0, 0);
816 SendResp_upnphttp(h);
817 CloseSocket_upnphttp(h);
820 static void
821 ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
823 enum event_type type;
824 DPRINTF(E_DEBUG, L_HTTP, "ProcessHTTPUnSubscribe %s\n", path);
825 DPRINTF(E_DEBUG, L_HTTP, "SID '%.*s'\n", h->req_SIDLen, h->req_SID);
826 /* Remove from the list */
827 type = check_event(h);
828 if (type != E_INVALID)
830 if(upnpevents_removeSubscriber(h->req_SID, h->req_SIDLen) < 0)
831 BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
832 else
833 BuildResp_upnphttp(h, 0, 0);
835 SendResp_upnphttp(h);
836 CloseSocket_upnphttp(h);
839 /* Parse and process Http Query
840 * called once all the HTTP headers have been received. */
841 static void
842 ProcessHttpQuery_upnphttp(struct upnphttp * h)
844 char HttpCommand[16];
845 char HttpUrl[512];
846 char * HttpVer;
847 char * p;
848 int i;
849 p = h->req_buf;
850 if(!p)
851 return;
852 for(i = 0; i<15 && *p && *p != ' ' && *p != '\r'; i++)
853 HttpCommand[i] = *(p++);
854 HttpCommand[i] = '\0';
855 while(*p==' ')
856 p++;
857 for(i = 0; i<511 && *p && *p != ' ' && *p != '\r'; i++)
858 HttpUrl[i] = *(p++);
859 HttpUrl[i] = '\0';
860 while(*p==' ')
861 p++;
862 HttpVer = h->HttpVer;
863 for(i = 0; i<15 && *p && *p != '\r'; i++)
864 HttpVer[i] = *(p++);
865 HttpVer[i] = '\0';
867 /* set the interface here initially, in case there is no Host header */
868 for(i = 0; i<n_lan_addr; i++)
870 if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
871 == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
873 h->iface = i;
874 break;
878 ParseHttpHeaders(h);
880 /* see if we need to wait for remaining data */
881 if( (h->reqflags & FLAG_CHUNKED) )
883 if( h->req_chunklen == -1)
885 Send400(h);
886 return;
888 if( h->req_chunklen )
890 h->state = 2;
891 return;
893 char *chunkstart, *chunk, *endptr, *endbuf;
894 chunk = endbuf = chunkstart = h->req_buf + h->req_contentoff;
896 while ((h->req_chunklen = strtol(chunk, &endptr, 16)) > 0 && (endptr != chunk) )
898 endptr = strstr(endptr, "\r\n");
899 if (!endptr)
901 Send400(h);
902 return;
904 endptr += 2;
906 memmove(endbuf, endptr, h->req_chunklen);
908 endbuf += h->req_chunklen;
909 chunk = endptr + h->req_chunklen;
911 h->req_contentlen = endbuf - chunkstart;
912 h->req_buflen = endbuf - h->req_buf;
913 h->state = 100;
916 DPRINTF(E_DEBUG, L_HTTP, "HTTP REQUEST: %.*s\n", h->req_buflen, h->req_buf);
917 if(h->req_Host && h->req_HostLen > 0) {
918 const char *port = memchr(h->req_Host, ':', h->req_HostLen);
919 size_t ip_sz = port ? (port - h->req_Host) : h->req_HostLen;
920 struct in_addr addr;
921 char ip_buf[16];
922 DPRINTF(E_MAXDEBUG, L_HTTP, "Host: %.*s\n", h->req_HostLen, h->req_Host);
923 if (port) {
924 const char *ptr = port + 1;
925 for (i = ip_sz + 2; i < h->req_HostLen; i++) {
926 if (*ptr > '9' || *ptr < '0')
927 break;
928 ptr++;
930 if (i != h->req_HostLen || atoi(port + 1) > 65535) {
931 DPRINTF(E_ERROR, L_HTTP, "DNS rebinding attack suspected (Host: %.*s)\n", h->req_HostLen, h->req_Host);
932 Send400(h);
933 return;
936 strncpyt(ip_buf, h->req_Host, MIN(ip_sz + 1, sizeof(ip_buf)));
937 if (ip_sz >= sizeof(ip_buf) || inet_pton(AF_INET, ip_buf, &addr) <= 0 || !addr.s_addr) {
938 DPRINTF(E_ERROR, L_HTTP, "DNS rebinding attack suspected (Host: %.*s)\n", h->req_HostLen, h->req_Host);
939 Send400(h);
940 return;
943 if(strcmp("POST", HttpCommand) == 0)
945 h->req_command = EPost;
946 ProcessHTTPPOST_upnphttp(h);
948 else if((strcmp("GET", HttpCommand) == 0) || (strcmp("HEAD", HttpCommand) == 0))
950 if( ((strcmp(h->HttpVer, "HTTP/1.1")==0) && !(h->reqflags & FLAG_HOST)) || (h->reqflags & FLAG_INVALID_REQ) )
952 DPRINTF(E_WARN, L_HTTP, "Invalid request, responding ERROR 400. (No Host specified in HTTP headers?)\n");
953 Send400(h);
954 return;
956 /* 7.3.33.4 */
957 else if( (h->reqflags & (FLAG_TIMESEEK|FLAG_PLAYSPEED)) &&
958 !(h->reqflags & FLAG_RANGE) )
960 DPRINTF(E_WARN, L_HTTP, "DLNA %s requested, responding ERROR 406\n",
961 h->reqflags&FLAG_TIMESEEK ? "TimeSeek" : "PlaySpeed");
962 Send406(h);
963 return;
965 else if(strcmp("GET", HttpCommand) == 0)
967 h->req_command = EGet;
969 else
971 h->req_command = EHead;
973 if(strcmp(ROOTDESC_PATH, HttpUrl) == 0)
975 /* If it's a Xbox360, we might need a special friendly_name to be recognized */
976 if( h->req_client && h->req_client->type->type == EXbox )
978 char model_sav[2];
979 i = 0;
980 memcpy(model_sav, modelnumber, 2);
981 strcpy(modelnumber, "1");
982 if( !strchr(friendly_name, ':') )
984 i = strlen(friendly_name);
985 snprintf(friendly_name+i, FRIENDLYNAME_MAX_LEN-i, ": 1");
987 sendXMLdesc(h, genRootDesc);
988 if( i )
989 friendly_name[i] = '\0';
990 memcpy(modelnumber, model_sav, 2);
992 else if( h->req_client && h->req_client->type->flags & FLAG_SAMSUNG_DCM10 )
994 sendXMLdesc(h, genRootDescSamsung);
996 else
998 sendXMLdesc(h, genRootDesc);
1001 else if(strcmp(CONTENTDIRECTORY_PATH, HttpUrl) == 0)
1003 sendXMLdesc(h, genContentDirectory);
1005 else if(strcmp(CONNECTIONMGR_PATH, HttpUrl) == 0)
1007 sendXMLdesc(h, genConnectionManager);
1009 else if(strcmp(X_MS_MEDIARECEIVERREGISTRAR_PATH, HttpUrl) == 0)
1011 sendXMLdesc(h, genX_MS_MediaReceiverRegistrar);
1013 else if(strncmp(HttpUrl, "/MediaItems/", 12) == 0)
1015 SendResp_dlnafile(h, HttpUrl+12);
1017 else if(strncmp(HttpUrl, "/Thumbnails/", 12) == 0)
1019 SendResp_thumbnail(h, HttpUrl+12);
1021 else if(strncmp(HttpUrl, "/AlbumArt/", 10) == 0)
1023 SendResp_albumArt(h, HttpUrl+10);
1025 #ifdef TIVO_SUPPORT
1026 else if(strncmp(HttpUrl, "/TiVoConnect", 12) == 0)
1028 if( GETFLAG(TIVO_MASK) )
1030 if( *(HttpUrl+12) == '?' )
1032 ProcessTiVoCommand(h, HttpUrl+13);
1034 else
1036 DPRINTF(E_WARN, L_HTTP, "Invalid TiVo request! %s\n", HttpUrl+12);
1037 Send404(h);
1040 else
1042 DPRINTF(E_WARN, L_HTTP, "TiVo request with out TiVo support enabled! %s\n",
1043 HttpUrl+12);
1044 Send404(h);
1047 #endif
1048 else if(strncmp(HttpUrl, "/Resized/", 9) == 0)
1050 SendResp_resizedimg(h, HttpUrl+9);
1052 else if(strncmp(HttpUrl, "/icons/", 7) == 0)
1054 SendResp_icon(h, HttpUrl+7);
1056 else if(strncmp(HttpUrl, "/Captions/", 10) == 0)
1058 SendResp_caption(h, HttpUrl+10);
1060 else if(strncmp(HttpUrl, "/status", 7) == 0)
1062 SendResp_presentation(h);
1064 else if(strcmp(HttpUrl, "/") == 0)
1066 #ifdef READYNAS
1067 SendResp_readynas_admin(h);
1068 #else
1069 SendResp_presentation(h);
1070 #endif
1072 else
1074 DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", HttpUrl);
1075 Send404(h);
1078 else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
1080 h->req_command = ESubscribe;
1081 ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
1083 else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
1085 h->req_command = EUnSubscribe;
1086 ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
1088 else
1090 DPRINTF(E_WARN, L_HTTP, "Unsupported HTTP Command %s\n", HttpCommand);
1091 Send501(h);
1095 static void
1096 Process_upnphttp(struct event *ev)
1098 char buf[2048];
1099 struct upnphttp *h = ev->data;
1100 int n;
1102 switch(h->state)
1104 case 0:
1105 n = recv(h->ev.fd, buf, 2048, 0);
1106 if(n<0)
1108 DPRINTF(E_ERROR, L_HTTP, "recv (state0): %s\n", strerror(errno));
1109 h->state = 100;
1111 else if(n==0)
1113 DPRINTF(E_DEBUG, L_HTTP, "HTTP Connection closed unexpectedly\n");
1114 h->state = 100;
1116 else
1118 int new_req_buflen;
1119 const char * endheaders;
1120 /* if 1st arg of realloc() is null,
1121 * realloc behaves the same as malloc() */
1122 new_req_buflen = n + h->req_buflen + 1;
1123 if (new_req_buflen >= 1024 * 1024)
1125 DPRINTF(E_ERROR, L_HTTP, "Receive headers too large (received %d bytes)\n", new_req_buflen);
1126 h->state = 100;
1127 break;
1129 h->req_buf = (char *)realloc(h->req_buf, new_req_buflen);
1130 if (!h->req_buf)
1132 DPRINTF(E_ERROR, L_HTTP, "Receive headers: %s\n", strerror(errno));
1133 h->state = 100;
1134 break;
1136 memcpy(h->req_buf + h->req_buflen, buf, n);
1137 h->req_buflen += n;
1138 h->req_buf[h->req_buflen] = '\0';
1139 /* search for the string "\r\n\r\n" */
1140 endheaders = strstr(h->req_buf, "\r\n\r\n");
1141 if(endheaders)
1143 h->req_contentoff = endheaders - h->req_buf + 4;
1144 h->req_contentlen = h->req_buflen - h->req_contentoff;
1145 ProcessHttpQuery_upnphttp(h);
1148 break;
1149 case 1:
1150 case 2:
1151 n = recv(h->ev.fd, buf, sizeof(buf), 0);
1152 if(n < 0)
1154 DPRINTF(E_ERROR, L_HTTP, "recv (state%d): %s\n", h->state, strerror(errno));
1155 h->state = 100;
1157 else if(n == 0)
1159 DPRINTF(E_DEBUG, L_HTTP, "HTTP Connection closed unexpectedly\n");
1160 h->state = 100;
1162 else
1164 buf[sizeof(buf)-1] = '\0';
1165 /*fwrite(buf, 1, n, stdout);*/ /* debug */
1166 h->req_buf = (char *)realloc(h->req_buf, n + h->req_buflen);
1167 if (!h->req_buf)
1169 DPRINTF(E_ERROR, L_HTTP, "Receive request body: %s\n", strerror(errno));
1170 h->state = 100;
1171 break;
1173 memcpy(h->req_buf + h->req_buflen, buf, n);
1174 h->req_buflen += n;
1175 if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
1177 /* Need the struct to point to the realloc'd memory locations */
1178 if( h->state == 1 )
1180 ParseHttpHeaders(h);
1181 ProcessHTTPPOST_upnphttp(h);
1183 else if( h->state == 2 )
1185 ProcessHttpQuery_upnphttp(h);
1189 break;
1190 default:
1191 DPRINTF(E_WARN, L_HTTP, "Unexpected state: %d\n", h->state);
1195 /* with response code and response message
1196 * also allocate enough memory */
1198 void
1199 BuildHeader_upnphttp(struct upnphttp * h, int respcode,
1200 const char * respmsg,
1201 int bodylen)
1203 static const char httpresphead[] =
1204 "%s %d %s\r\n"
1205 "Content-Type: %s\r\n"
1206 "Connection: close\r\n"
1207 "Content-Length: %d\r\n"
1208 "Server: " MINIDLNA_SERVER_STRING "\r\n";
1209 time_t curtime = time(NULL);
1210 char date[30];
1211 int templen;
1212 struct string_s res;
1213 if(!h->res_buf)
1215 templen = sizeof(httpresphead) + 256 + bodylen;
1216 h->res_buf = (char *)malloc(templen);
1217 h->res_buf_alloclen = templen;
1219 res.data = h->res_buf;
1220 res.size = h->res_buf_alloclen;
1221 res.off = 0;
1222 strcatf(&res, httpresphead, "HTTP/1.1",
1223 respcode, respmsg,
1224 (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
1225 bodylen);
1226 /* Additional headers */
1227 if(h->respflags & FLAG_TIMEOUT) {
1228 strcatf(&res, "Timeout: Second-");
1229 if(h->req_Timeout) {
1230 strcatf(&res, "%d\r\n", h->req_Timeout);
1231 } else {
1232 strcatf(&res, "300\r\n");
1235 if(h->respflags & FLAG_SID) {
1236 strcatf(&res, "SID: %.*s\r\n", h->req_SIDLen, h->req_SID);
1238 if(h->reqflags & FLAG_LANGUAGE) {
1239 strcatf(&res, "Content-Language: en\r\n");
1241 strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
1242 strcatf(&res, "Date: %s\r\n", date);
1243 strcatf(&res, "EXT:\r\n");
1244 strcatf(&res, "\r\n");
1245 h->res_buflen = res.off;
1246 if(h->res_buf_alloclen < (h->res_buflen + bodylen))
1248 h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
1249 h->res_buf_alloclen = h->res_buflen + bodylen;
1253 void
1254 BuildResp2_upnphttp(struct upnphttp * h, int respcode,
1255 const char * respmsg,
1256 const char * body, int bodylen)
1258 BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
1259 if( h->req_command == EHead )
1260 return;
1261 if(body)
1262 memcpy(h->res_buf + h->res_buflen, body, bodylen);
1263 h->res_buflen += bodylen;
1266 /* responding 200 OK ! */
1267 void
1268 BuildResp_upnphttp(struct upnphttp *h, const char *body, int bodylen)
1270 BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
1273 void
1274 SendResp_upnphttp(struct upnphttp * h)
1276 int n;
1277 DPRINTF(E_DEBUG, L_HTTP, "HTTP RESPONSE: %.*s\n", h->res_buflen, h->res_buf);
1278 n = send(h->ev.fd, h->res_buf, h->res_buflen, 0);
1279 if(n<0)
1281 DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %s\n", strerror(errno));
1283 else if(n < h->res_buflen)
1285 /* TODO : handle correctly this case */
1286 DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %d bytes sent (out of %d)\n",
1287 n, h->res_buflen);
1291 static int
1292 send_data(struct upnphttp * h, char * header, size_t size, int flags)
1294 int n;
1296 n = send(h->ev.fd, header, size, flags);
1297 if(n<0)
1299 DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %s\n", strerror(errno));
1301 else if(n < h->res_buflen)
1303 /* TODO : handle correctly this case */
1304 DPRINTF(E_ERROR, L_HTTP, "send(res_buf): %d bytes sent (out of %d)\n",
1305 n, h->res_buflen);
1307 else
1309 return 0;
1311 return 1;
1314 static void
1315 send_file(struct upnphttp * h, int sendfd, off_t offset, off_t end_offset)
1317 off_t send_size;
1318 off_t ret;
1319 char *buf = NULL;
1320 #if HAVE_SENDFILE
1321 int try_sendfile = 1;
1322 #endif
1324 while( offset <= end_offset )
1326 #if HAVE_SENDFILE
1327 if( try_sendfile )
1329 send_size = ( ((end_offset - offset) < MAX_BUFFER_SIZE) ? (end_offset - offset + 1) : MAX_BUFFER_SIZE);
1330 ret = sys_sendfile(h->ev.fd, sendfd, &offset, send_size);
1331 if( ret == -1 )
1333 DPRINTF(E_DEBUG, L_HTTP, "sendfile error :: error no. %d [%s]\n", errno, strerror(errno));
1334 /* If sendfile isn't supported on the filesystem, don't bother trying to use it again. */
1335 if( errno == EOVERFLOW || errno == EINVAL )
1336 try_sendfile = 0;
1337 else if( errno != EAGAIN )
1338 break;
1340 else if( ret == 0 )
1342 break; /* Premature end of file */
1344 else
1346 //DPRINTF(E_DEBUG, L_HTTP, "sent %lld bytes to %d. offset is now %lld.\n", ret, h->socket, offset);
1347 continue;
1350 #endif
1351 /* Fall back to regular I/O */
1352 if( !buf )
1353 buf = malloc(MIN_BUFFER_SIZE);
1354 send_size = (((end_offset - offset) < MIN_BUFFER_SIZE) ? (end_offset - offset + 1) : MIN_BUFFER_SIZE);
1355 lseek(sendfd, offset, SEEK_SET);
1356 ret = read(sendfd, buf, send_size);
1357 if( ret == -1 ) {
1358 DPRINTF(E_DEBUG, L_HTTP, "read error :: error no. %d [%s]\n", errno, strerror(errno));
1359 if( errno == EAGAIN )
1360 continue;
1361 else
1362 break;
1364 else if( ret == 0 )
1366 break; /* premature end of file */
1368 ret = write(h->ev.fd, buf, ret);
1369 if( ret == -1 ) {
1370 DPRINTF(E_DEBUG, L_HTTP, "write error :: error no. %d [%s]\n", errno, strerror(errno));
1371 if( errno == EAGAIN )
1372 continue;
1373 else
1374 break;
1376 offset += ret;
1378 free(buf);
1381 static void
1382 start_dlna_header(struct string_s *str, int respcode, const char *tmode, const char *mime)
1384 char date[30];
1385 time_t now;
1387 now = time(NULL);
1388 strftime(date, sizeof(date),"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&now));
1389 strcatf(str, "HTTP/1.1 %d OK\r\n"
1390 "Connection: close\r\n"
1391 "Date: %s\r\n"
1392 "Server: " MINIDLNA_SERVER_STRING "\r\n"
1393 "EXT:\r\n"
1394 "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n"
1395 "transferMode.dlna.org: %s\r\n"
1396 "Content-Type: %s\r\n",
1397 respcode, date, tmode, mime);
1400 static int
1401 _open_file(const char *orig_path)
1403 struct media_dir_s *media_path;
1404 char buf[PATH_MAX];
1405 const char *path;
1406 int fd;
1408 if (!GETFLAG(WIDE_LINKS_MASK))
1410 path = realpath(orig_path, buf);
1411 if (!path)
1413 DPRINTF(E_ERROR, L_HTTP, "Error resolving path %s: %s\n",
1414 orig_path, strerror(errno));
1415 return -1;
1418 for (media_path = media_dirs; media_path; media_path = media_path->next)
1420 if (strncmp(path, media_path->path, strlen(media_path->path)) == 0)
1421 break;
1423 if (!media_path && strncmp(path, db_path, strlen(db_path)))
1425 DPRINTF(E_ERROR, L_HTTP, "Rejecting wide link %s -> %s\n",
1426 orig_path, path);
1427 return -403;
1430 else
1431 path = orig_path;
1433 fd = open(path, O_RDONLY);
1434 if (fd < 0)
1435 DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
1437 return fd;
1440 static void
1441 SendResp_icon(struct upnphttp * h, char * icon)
1443 char header[512];
1444 char mime[12] = "image/";
1445 char *data;
1446 int size;
1447 struct string_s str;
1449 if( strcmp(icon, "sm.png") == 0 )
1451 DPRINTF(E_DEBUG, L_HTTP, "Sending small PNG icon\n");
1452 data = (char *)png_sm;
1453 size = sizeof(png_sm)-1;
1454 strcpy(mime+6, "png");
1456 else if( strcmp(icon, "lrg.png") == 0 )
1458 DPRINTF(E_DEBUG, L_HTTP, "Sending large PNG icon\n");
1459 data = (char *)png_lrg;
1460 size = sizeof(png_lrg)-1;
1461 strcpy(mime+6, "png");
1463 else if( strcmp(icon, "sm.jpg") == 0 )
1465 DPRINTF(E_DEBUG, L_HTTP, "Sending small JPEG icon\n");
1466 data = (char *)jpeg_sm;
1467 size = sizeof(jpeg_sm)-1;
1468 strcpy(mime+6, "jpeg");
1470 else if( strcmp(icon, "lrg.jpg") == 0 )
1472 DPRINTF(E_DEBUG, L_HTTP, "Sending large JPEG icon\n");
1473 data = (char *)jpeg_lrg;
1474 size = sizeof(jpeg_lrg)-1;
1475 strcpy(mime+6, "jpeg");
1477 else
1479 DPRINTF(E_WARN, L_HTTP, "Invalid icon request: %s\n", icon);
1480 Send404(h);
1481 return;
1484 INIT_STR(str, header);
1486 start_dlna_header(&str, 200, "Interactive", mime);
1487 strcatf(&str, "Content-Length: %d\r\n\r\n", size);
1489 if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1491 if( h->req_command != EHead )
1492 send_data(h, data, size, 0);
1494 CloseSocket_upnphttp(h);
1497 static void
1498 SendResp_albumArt(struct upnphttp * h, char * object)
1500 char header[512];
1501 char *path;
1502 off_t size;
1503 long long id;
1504 int fd;
1505 struct string_s str;
1507 if( h->reqflags & (FLAG_XFERSTREAMING|FLAG_RANGE) )
1509 DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
1510 Send406(h);
1511 return;
1514 id = strtoll(object, NULL, 10);
1516 path = sql_get_text_field(db, "SELECT PATH from ALBUM_ART where ID = '%lld'", id);
1517 if( !path )
1519 DPRINTF(E_WARN, L_HTTP, "ALBUM_ART ID %s not found, responding ERROR 404\n", object);
1520 Send404(h);
1521 return;
1523 DPRINTF(E_INFO, L_HTTP, "Serving album art ID: %lld [%s]\n", id, path);
1525 fd = _open_file(path);
1526 if( fd < 0 ) {
1527 sqlite3_free(path);
1528 if (fd == -403)
1529 Send403(h);
1530 else
1531 Send404(h);
1532 return;
1534 sqlite3_free(path);
1535 size = lseek(fd, 0, SEEK_END);
1536 lseek(fd, 0, SEEK_SET);
1538 INIT_STR(str, header);
1540 start_dlna_header(&str, 200, "Interactive", "image/jpeg");
1541 strcatf(&str, "Content-Length: %jd\r\n"
1542 "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n\r\n",
1543 (intmax_t)size);
1545 if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1547 if( h->req_command != EHead )
1548 send_file(h, fd, 0, size-1);
1550 close(fd);
1551 CloseSocket_upnphttp(h);
1554 static void
1555 SendResp_caption(struct upnphttp * h, char * object)
1557 char header[512];
1558 char *path;
1559 off_t size;
1560 long long id;
1561 int fd;
1562 struct string_s str;
1564 id = strtoll(object, NULL, 10);
1566 path = sql_get_text_field(db, "SELECT PATH from CAPTIONS where ID = %lld", id);
1567 if( !path )
1569 DPRINTF(E_WARN, L_HTTP, "CAPTION ID %s not found, responding ERROR 404\n", object);
1570 Send404(h);
1571 return;
1573 DPRINTF(E_INFO, L_HTTP, "Serving caption ID: %lld [%s]\n", id, path);
1575 fd = _open_file(path);
1576 if( fd < 0 ) {
1577 sqlite3_free(path);
1578 if (fd == -403)
1579 Send403(h);
1580 else
1581 Send404(h);
1582 return;
1584 sqlite3_free(path);
1585 size = lseek(fd, 0, SEEK_END);
1586 lseek(fd, 0, SEEK_SET);
1588 INIT_STR(str, header);
1590 start_dlna_header(&str, 200, "Interactive", "smi/caption");
1591 strcatf(&str, "Content-Length: %jd\r\n\r\n", (intmax_t)size);
1593 if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1595 if( h->req_command != EHead )
1596 send_file(h, fd, 0, size-1);
1598 close(fd);
1599 CloseSocket_upnphttp(h);
1602 static void
1603 SendResp_thumbnail(struct upnphttp * h, char * object)
1605 char header[512];
1606 char *path;
1607 long long id;
1608 ExifData *ed;
1609 ExifLoader *l;
1610 struct string_s str;
1612 if( h->reqflags & (FLAG_XFERSTREAMING|FLAG_RANGE) )
1614 DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
1615 Send406(h);
1616 return;
1619 id = strtoll(object, NULL, 10);
1620 path = sql_get_text_field(db, "SELECT PATH from DETAILS where ID = '%lld'", id);
1621 if( !path )
1623 DPRINTF(E_WARN, L_HTTP, "DETAIL ID %s not found, responding ERROR 404\n", object);
1624 Send404(h);
1625 return;
1627 DPRINTF(E_INFO, L_HTTP, "Serving thumbnail for ObjectId: %lld [%s]\n", id, path);
1629 if( access(path, F_OK) != 0 )
1631 DPRINTF(E_ERROR, L_HTTP, "Error accessing %s\n", path);
1632 Send404(h);
1633 sqlite3_free(path);
1634 return;
1637 l = exif_loader_new();
1638 exif_loader_write_file(l, path);
1639 ed = exif_loader_get_data(l);
1640 exif_loader_unref(l);
1641 sqlite3_free(path);
1643 if( !ed || !ed->size )
1645 Send404(h);
1646 if( ed )
1647 exif_data_unref(ed);
1648 return;
1651 INIT_STR(str, header);
1653 start_dlna_header(&str, 200, "Interactive", "image/jpeg");
1654 strcatf(&str, "Content-Length: %jd\r\n"
1655 "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN;DLNA.ORG_CI=1\r\n\r\n",
1656 (intmax_t)ed->size);
1658 if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
1660 if( h->req_command != EHead )
1661 send_data(h, (char *)ed->data, ed->size, 0);
1663 exif_data_unref(ed);
1664 CloseSocket_upnphttp(h);
1667 static void
1668 SendResp_resizedimg(struct upnphttp * h, char * object)
1670 char header[512];
1671 char buf[128];
1672 struct string_s str;
1673 char **result;
1674 char dlna_pn[22];
1675 uint32_t dlna_flags = DLNA_FLAG_DLNA_V1_5|DLNA_FLAG_HTTP_STALLING|DLNA_FLAG_TM_B|DLNA_FLAG_TM_I;
1676 int width=640, height=480, dstw, dsth, size;
1677 int srcw, srch;
1678 unsigned char * data = NULL;
1679 char *path, *file_path = NULL;
1680 char *resolution = NULL;
1681 char *key, *val;
1682 char *saveptr, *item = NULL;
1683 int rotate = 0;
1684 int pixw = 0, pixh = 0;
1685 long long id;
1686 int rows=0, chunked, ret;
1687 image_s *imsrc = NULL, *imdst = NULL;
1688 int scale = 1;
1689 const char *tmode;
1691 id = strtoll(object, &saveptr, 10);
1692 snprintf(buf, sizeof(buf), "SELECT PATH, RESOLUTION, ROTATION from DETAILS where ID = '%lld'", (long long)id);
1693 ret = sql_get_table(db, buf, &result, &rows, NULL);
1694 if( ret != SQLITE_OK )
1696 Send500(h);
1697 return;
1699 if( rows )
1701 file_path = result[3];
1702 resolution = result[4];
1703 if (result[5])
1704 rotate = atoi(result[5]);
1706 if( !file_path || !resolution || (access(file_path, F_OK) != 0) )
1708 DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
1709 sqlite3_free_table(result);
1710 Send404(h);
1711 return;
1714 if( saveptr )
1715 saveptr = strchr(saveptr, '?');
1716 path = saveptr ? saveptr + 1 : object;
1717 for( item = strtok_r(path, "&,", &saveptr); item != NULL; item = strtok_r(NULL, "&,", &saveptr) )
1719 decodeString(item, 1);
1720 val = item;
1721 key = strsep(&val, "=");
1722 if( !val )
1723 continue;
1724 DPRINTF(E_DEBUG, L_GENERAL, "%s: %s\n", key, val);
1725 if( strcasecmp(key, "width") == 0 )
1727 width = atoi(val);
1729 else if( strcasecmp(key, "height") == 0 )
1731 height = atoi(val);
1733 else if( strcasecmp(key, "rotation") == 0 )
1735 rotate = (rotate + atoi(val)) % 360;
1736 sql_exec(db, "UPDATE DETAILS set ROTATION = %d where ID = %lld", rotate, id);
1738 else if( strcasecmp(key, "pixelshape") == 0 )
1740 ret = sscanf(val, "%d:%d", &pixw, &pixh);
1741 if( ret != 2 )
1742 pixw = pixh = 0;
1746 #if USE_FORK
1747 pid_t newpid = 0;
1748 newpid = process_fork(h->req_client);
1749 if( newpid > 0 )
1751 CloseSocket_upnphttp(h);
1752 goto resized_error;
1754 #endif
1755 if( h->reqflags & (FLAG_XFERSTREAMING|FLAG_RANGE) )
1757 DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
1758 Send406(h);
1759 goto resized_error;
1762 DPRINTF(E_INFO, L_HTTP, "Serving resized image for ObjectId: %lld [%s]\n", id, file_path);
1763 if( rotate )
1764 DPRINTF(E_DEBUG, L_HTTP, "Rotating image %d degrees\n", rotate);
1765 switch( rotate )
1767 case 90:
1768 ret = sscanf(resolution, "%dx%d", &srch, &srcw);
1769 rotate = ROTATE_90;
1770 break;
1771 case 270:
1772 ret = sscanf(resolution, "%dx%d", &srch, &srcw);
1773 rotate = ROTATE_270;
1774 break;
1775 case 180:
1776 ret = sscanf(resolution, "%dx%d", &srcw, &srch);
1777 rotate = ROTATE_180;
1778 break;
1779 default:
1780 ret = sscanf(resolution, "%dx%d", &srcw, &srch);
1781 rotate = ROTATE_NONE;
1782 break;
1784 if( ret != 2 )
1786 Send500(h);
1787 goto resized_error;
1789 /* Figure out the best destination resolution we can use */
1790 dstw = width;
1791 dsth = ((((width<<10)/srcw)*srch)>>10);
1792 if( dsth > height )
1794 dsth = height;
1795 dstw = (((height<<10)/srch) * srcw>>10);
1797 /* Account for pixel shape */
1798 if( pixw && pixh )
1800 if( pixh > pixw )
1801 dsth = dsth * pixw / pixh;
1802 else if( pixw > pixh )
1803 dstw = dstw * pixh / pixw;
1806 if( dstw <= 160 && dsth <= 160 )
1807 strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_TN;");
1808 else if( dstw <= 640 && dsth <= 480 )
1809 strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_SM;");
1810 else if( dstw <= 1024 && dsth <= 768 )
1811 strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_MED;");
1812 else
1813 strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_LRG;");
1815 if( srcw>>4 >= dstw && srch>>4 >= dsth)
1816 scale = 8;
1817 else if( srcw>>3 >= dstw && srch>>3 >= dsth )
1818 scale = 4;
1819 else if( srcw>>2 >= dstw && srch>>2 >= dsth )
1820 scale = 2;
1822 INIT_STR(str, header);
1824 #if USE_FORK
1825 if( (h->reqflags & FLAG_XFERBACKGROUND) && (setpriority(PRIO_PROCESS, 0, 19) == 0) )
1826 tmode = "Background";
1827 else
1828 #endif
1829 tmode = "Interactive";
1830 start_dlna_header(&str, 200, tmode, "image/jpeg");
1831 strcatf(&str, "contentFeatures.dlna.org: %sDLNA.ORG_CI=1;DLNA.ORG_FLAGS=%08X%024X\r\n",
1832 dlna_pn, dlna_flags, 0);
1834 if( strcmp(h->HttpVer, "HTTP/1.0") == 0 )
1836 chunked = 0;
1837 imsrc = image_new_from_jpeg(file_path, 1, NULL, 0, scale, rotate);
1839 else
1841 chunked = 1;
1842 strcatf(&str, "Transfer-Encoding: chunked\r\n\r\n");
1845 if( !chunked )
1847 if( !imsrc )
1849 DPRINTF(E_WARN, L_HTTP, "Unable to open image %s!\n", file_path);
1850 Send500(h);
1851 goto resized_error;
1854 imdst = image_resize(imsrc, dstw, dsth);
1855 data = image_save_to_jpeg_buf(imdst, &size);
1857 strcatf(&str, "Content-Length: %d\r\n\r\n", size);
1860 if( (send_data(h, str.data, str.off, 0) == 0) && (h->req_command != EHead) )
1862 if( chunked )
1864 imsrc = image_new_from_jpeg(file_path, 1, NULL, 0, scale, rotate);
1865 if( !imsrc )
1867 DPRINTF(E_WARN, L_HTTP, "Unable to open image %s!\n", file_path);
1868 Send500(h);
1869 goto resized_error;
1871 imdst = image_resize(imsrc, dstw, dsth);
1872 data = image_save_to_jpeg_buf(imdst, &size);
1874 ret = sprintf(buf, "%x\r\n", size);
1875 send_data(h, buf, ret, MSG_MORE);
1876 send_data(h, (char *)data, size, MSG_MORE);
1877 send_data(h, "\r\n0\r\n\r\n", 7, 0);
1879 else
1881 send_data(h, (char *)data, size, 0);
1884 DPRINTF(E_INFO, L_HTTP, "Done serving %s\n", file_path);
1885 if( imsrc )
1886 image_free(imsrc);
1887 if( imdst )
1888 image_free(imdst);
1889 CloseSocket_upnphttp(h);
1890 resized_error:
1891 sqlite3_free_table(result);
1892 #if USE_FORK
1893 if( newpid == 0 )
1894 _exit(0);
1895 #endif
1898 static void
1899 SendResp_dlnafile(struct upnphttp *h, char *object)
1901 char header[1024];
1902 struct string_s str;
1903 char buf[128];
1904 char **result;
1905 int rows, ret;
1906 off_t total, offset, size;
1907 int64_t id;
1908 int sendfh;
1909 uint32_t dlna_flags = DLNA_FLAG_DLNA_V1_5|DLNA_FLAG_HTTP_STALLING|DLNA_FLAG_TM_B;
1910 uint32_t cflags = h->req_client ? h->req_client->type->flags : 0;
1911 const char *tmode;
1912 enum client_types ctype = h->req_client ? h->req_client->type->type : 0;
1913 static struct { int64_t id;
1914 enum client_types client;
1915 char path[PATH_MAX];
1916 char mime[32];
1917 char dlna[96];
1918 } last_file = { 0, 0 };
1919 #if USE_FORK
1920 pid_t newpid = 0;
1921 #endif
1923 id = strtoll(object, NULL, 10);
1924 if( cflags & FLAG_MS_PFS )
1926 if( strstr(object, "?albumArt=true") )
1928 char *art;
1929 art = sql_get_text_field(db, "SELECT ALBUM_ART from DETAILS where ID = '%lld'", id);
1930 if (art)
1932 SendResp_albumArt(h, art);
1933 sqlite3_free(art);
1935 else
1936 Send404(h);
1937 return;
1940 if( id != last_file.id || ctype != last_file.client )
1942 snprintf(buf, sizeof(buf), "SELECT PATH, MIME, DLNA_PN from DETAILS where ID = '%lld'", (long long)id);
1943 ret = sql_get_table(db, buf, &result, &rows, NULL);
1944 if( (ret != SQLITE_OK) )
1946 DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %lld!\n", (long long)id);
1947 Send500(h);
1948 return;
1950 if( !rows || !result[3] || !result[4] )
1952 DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object);
1953 sqlite3_free_table(result);
1954 Send404(h);
1955 return;
1957 /* Cache the result */
1958 last_file.id = id;
1959 last_file.client = ctype;
1960 strncpy(last_file.path, result[3], sizeof(last_file.path)-1);
1961 if( result[4] )
1963 strncpy(last_file.mime, result[4], sizeof(last_file.mime)-1);
1964 /* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */
1965 if( cflags & FLAG_SAMSUNG )
1967 if( strcmp(last_file.mime+6, "x-matroska") == 0 )
1968 strcpy(last_file.mime+8, "mkv");
1969 /* Samsung TV's such as the A750 can natively support many
1970 Xvid/DivX AVI's however, the DLNA server needs the
1971 mime type to say video/mpeg */
1972 else if( ctype == ESamsungSeriesA && strcmp(last_file.mime+6, "x-msvideo") == 0 )
1973 strcpy(last_file.mime+6, "mpeg");
1975 /* ... and Sony BDP-S370 won't play MKV unless we pretend it's a DiVX file */
1976 else if( ctype == ESonyBDP )
1978 if( strcmp(last_file.mime+6, "x-matroska") == 0 ||
1979 strcmp(last_file.mime+6, "mpeg") == 0 )
1980 strcpy(last_file.mime+6, "divx");
1983 if( result[5] )
1984 snprintf(last_file.dlna, sizeof(last_file.dlna), "DLNA.ORG_PN=%s;", result[5]);
1985 else
1986 last_file.dlna[0] = '\0';
1987 sqlite3_free_table(result);
1989 #if USE_FORK
1990 newpid = process_fork(h->req_client);
1991 if( newpid > 0 )
1993 CloseSocket_upnphttp(h);
1994 return;
1996 #endif
1998 DPRINTF(E_INFO, L_HTTP, "Serving DetailID: %lld [%s]\n", (long long)id, last_file.path);
2000 if( h->reqflags & FLAG_XFERSTREAMING )
2002 if( strncmp(last_file.mime, "image", 5) == 0 )
2004 DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n");
2005 Send406(h);
2006 goto error;
2009 else if( h->reqflags & FLAG_XFERINTERACTIVE )
2011 if( h->reqflags & FLAG_REALTIMEINFO )
2013 DPRINTF(E_WARN, L_HTTP, "Bad realTimeInfo flag with Interactive request!\n");
2014 Send400(h);
2015 goto error;
2017 if( strncmp(last_file.mime, "image", 5) != 0 )
2019 DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Interactive without an image!\n");
2020 /* Samsung TVs (well, at least the A950) do this for some reason,
2021 * and I don't see them fixing this bug any time soon. */
2022 if( !(cflags & FLAG_SAMSUNG) || GETFLAG(DLNA_STRICT_MASK) )
2024 Send406(h);
2025 goto error;
2030 offset = h->req_RangeStart;
2031 sendfh = _open_file(last_file.path);
2032 if( sendfh < 0 ) {
2033 if (sendfh == -403)
2034 Send403(h);
2035 else
2036 Send404(h);
2037 goto error;
2039 size = lseek(sendfh, 0, SEEK_END);
2040 lseek(sendfh, 0, SEEK_SET);
2042 INIT_STR(str, header);
2044 #if USE_FORK
2045 if( (h->reqflags & FLAG_XFERBACKGROUND) && (setpriority(PRIO_PROCESS, 0, 19) == 0) )
2046 tmode = "Background";
2047 else
2048 #endif
2049 if( strncmp(last_file.mime, "image", 5) == 0 )
2050 tmode = "Interactive";
2051 else
2052 tmode = "Streaming";
2054 start_dlna_header(&str, (h->reqflags & FLAG_RANGE ? 206 : 200), tmode, last_file.mime);
2056 if( h->reqflags & FLAG_RANGE )
2058 if( !h->req_RangeEnd || h->req_RangeEnd == size )
2060 h->req_RangeEnd = size - 1;
2062 if( (h->req_RangeStart > h->req_RangeEnd) || (h->req_RangeStart < 0) )
2064 DPRINTF(E_WARN, L_HTTP, "Specified range was invalid!\n");
2065 Send400(h);
2066 close(sendfh);
2067 goto error;
2069 if( h->req_RangeEnd >= size )
2071 DPRINTF(E_WARN, L_HTTP, "Specified range was outside file boundaries!\n");
2072 Send416(h);
2073 close(sendfh);
2074 goto error;
2077 total = h->req_RangeEnd - h->req_RangeStart + 1;
2078 strcatf(&str, "Content-Length: %jd\r\n"
2079 "Content-Range: bytes %jd-%jd/%jd\r\n",
2080 (intmax_t)total, (intmax_t)h->req_RangeStart,
2081 (intmax_t)h->req_RangeEnd, (intmax_t)size);
2083 else
2085 h->req_RangeEnd = size - 1;
2086 total = size;
2087 strcatf(&str, "Content-Length: %jd\r\n", (intmax_t)total);
2090 switch( *last_file.mime )
2092 case 'i':
2093 dlna_flags |= DLNA_FLAG_TM_I;
2094 break;
2095 case 'a':
2096 case 'v':
2097 default:
2098 dlna_flags |= DLNA_FLAG_TM_S;
2099 break;
2102 if( h->reqflags & FLAG_CAPTION )
2104 if( sql_get_int_field(db, "SELECT ID from CAPTIONS where ID = '%lld'", (long long)id) > 0 )
2105 strcatf(&str, "CaptionInfo.sec: http://%s:%d/Captions/%lld.srt\r\n",
2106 lan_addr[h->iface].str, runtime_vars.port, (long long)id);
2109 strcatf(&str, "Accept-Ranges: bytes\r\n"
2110 "contentFeatures.dlna.org: %sDLNA.ORG_OP=%02X;DLNA.ORG_CI=%X;DLNA.ORG_FLAGS=%08X%024X\r\n\r\n",
2111 last_file.dlna, 1, 0, dlna_flags, 0);
2113 //DEBUG DPRINTF(E_DEBUG, L_HTTP, "RESPONSE: %s\n", str.data);
2114 if( send_data(h, str.data, str.off, MSG_MORE) == 0 )
2116 if( h->req_command != EHead )
2117 send_file(h, sendfh, offset, h->req_RangeEnd);
2119 close(sendfh);
2121 CloseSocket_upnphttp(h);
2122 error:
2123 #if USE_FORK
2124 if( newpid == 0 )
2125 _exit(0);
2126 #endif
2127 return;