source: titan/minidlna-1.0.22/upnphttp.c @ 13567

Last change on this file since 13567 was 13567, checked in by obi, 11 years ago

[titan] add minidlna-1.0.22 first step

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