1///////////////////////////////////////////////////////////////////////////
2//
3// Copyright (c) 2000-2003 Intel Corporation
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are met:
8//
9// * Redistributions of source code must retain the above copyright notice,
10// this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above copyright notice,
12// this list of conditions and the following disclaimer in the documentation
13// and/or other materials provided with the distribution.
14// * Neither name of Intel Corporation nor the names of its contributors
15// may be used to endorse or promote products derived from this software
16// without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
22// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30///////////////////////////////////////////////////////////////////////////
31
32/************************************************************************
33* Purpose: This file defines the Web Server and has functions to carry out
34* operations of the Web Server.
35************************************************************************/
36
37#include <assert.h>
38#include "util.h"
39#include "strintmap.h"
40#include "membuffer.h"
41#include "httpparser.h"
42#include "httpreadwrite.h"
43#include "statcodes.h"
44#include "webserver.h"
45#include "upnp.h"
46#include "config.h"
47#include "upnpapi.h"
48
49#include <unistd.h>
50#include <sys/stat.h>
51#include "ithread.h"
52#include "unixutil.h"
53
54/*
55   Response Types
56 */
57enum resp_type { RESP_FILEDOC, RESP_XMLDOC, RESP_HEADERS, RESP_WEBDOC,
58        RESP_POST };
59
60// mapping of file extension to content-type of document
61struct document_type_t {
62    const char *file_ext;
63    const char *content_type;
64    const char *content_subtype;
65};
66
67struct xml_alias_t {
68    membuffer name;             // name of DOC from root; e.g.: /foo/bar/mydesc.xml
69    membuffer doc;              // the XML document contents
70    time_t last_modified;
71    int *ct;
72};
73
74static const char *gMediaTypes[] = {
75    NULL,                       // 0
76    "audio",                    // 1
77    "video",                    // 2
78    "image",                    // 3
79    "application",              // 4
80    "text"                      // 5
81};
82
83/*
84   Defines
85 */
86
87// index into 'gMediaTypes'
88#define AUDIO_STR		"\1"
89#define VIDEO_STR		"\2"
90#define IMAGE_STR		"\3"
91#define APPLICATION_STR "\4"
92#define TEXT_STR		"\5"
93
94// int index
95#define APPLICATION_INDEX	4
96#define TEXT_INDEX			5
97
98// general
99#define NUM_MEDIA_TYPES 69
100#define NUM_HTTP_HEADER_NAMES 33
101
102// sorted by file extension; must have 'NUM_MEDIA_TYPES' extensions
103static const char *gEncodedMediaTypes =
104    "aif\0" AUDIO_STR "aiff\0"
105    "aifc\0" AUDIO_STR "aiff\0"
106    "aiff\0" AUDIO_STR "aiff\0"
107    "asf\0" VIDEO_STR "x-ms-asf\0"
108    "asx\0" VIDEO_STR "x-ms-asf\0"
109    "au\0" AUDIO_STR "basic\0"
110    "avi\0" VIDEO_STR "msvideo\0"
111    "bmp\0" IMAGE_STR "bmp\0"
112    "dcr\0" APPLICATION_STR "x-director\0"
113    "dib\0" IMAGE_STR "bmp\0"
114    "dir\0" APPLICATION_STR "x-director\0"
115    "dxr\0" APPLICATION_STR "x-director\0"
116    "gif\0" IMAGE_STR "gif\0"
117    "hta\0" TEXT_STR "hta\0"
118    "htm\0" TEXT_STR "html\0"
119    "html\0" TEXT_STR "html\0"
120    "jar\0" APPLICATION_STR "java-archive\0"
121    "jfif\0" IMAGE_STR "pjpeg\0"
122    "jpe\0" IMAGE_STR "jpeg\0"
123    "jpeg\0" IMAGE_STR "jpeg\0"
124    "jpg\0" IMAGE_STR "jpeg\0"
125    "js\0" APPLICATION_STR "x-javascript\0"
126    "kar\0" AUDIO_STR "midi\0"
127    "m3u\0" AUDIO_STR "mpegurl\0"
128    "mid\0" AUDIO_STR "midi\0"
129    "midi\0" AUDIO_STR "midi\0"
130    "mov\0" VIDEO_STR "quicktime\0"
131    "mp2v\0" VIDEO_STR "x-mpeg2\0"
132    "mp3\0" AUDIO_STR "mpeg\0"
133    "mpe\0" VIDEO_STR "mpeg\0"
134    "mpeg\0" VIDEO_STR "mpeg\0"
135    "mpg\0" VIDEO_STR "mpeg\0"
136    "mpv\0" VIDEO_STR "mpeg\0"
137    "mpv2\0" VIDEO_STR "x-mpeg2\0"
138    "pdf\0" APPLICATION_STR "pdf\0"
139    "pjp\0" IMAGE_STR "jpeg\0"
140    "pjpeg\0" IMAGE_STR "jpeg\0"
141    "plg\0" TEXT_STR "html\0"
142    "pls\0" AUDIO_STR "scpls\0"
143    "png\0" IMAGE_STR "png\0"
144    "qt\0" VIDEO_STR "quicktime\0"
145    "ram\0" AUDIO_STR "x-pn-realaudio\0"
146    "rmi\0" AUDIO_STR "mid\0"
147    "rmm\0" AUDIO_STR "x-pn-realaudio\0"
148    "rtf\0" APPLICATION_STR "rtf\0"
149    "shtml\0" TEXT_STR "html\0"
150    "smf\0" AUDIO_STR "midi\0"
151    "snd\0" AUDIO_STR "basic\0"
152    "spl\0" APPLICATION_STR "futuresplash\0"
153    "ssm\0" APPLICATION_STR "streamingmedia\0"
154    "swf\0" APPLICATION_STR "x-shockwave-flash\0"
155    "tar\0" APPLICATION_STR "tar\0"
156    "tcl\0" APPLICATION_STR "x-tcl\0"
157    "text\0" TEXT_STR "plain\0"
158    "tif\0" IMAGE_STR "tiff\0"
159    "tiff\0" IMAGE_STR "tiff\0"
160    "txt\0" TEXT_STR "plain\0"
161    "ulw\0" AUDIO_STR "basic\0"
162    "wav\0" AUDIO_STR "wav\0"
163    "wax\0" AUDIO_STR "x-ms-wax\0"
164    "wm\0" VIDEO_STR "x-ms-wm\0"
165    "wma\0" AUDIO_STR "x-ms-wma\0"
166    "wmv\0" VIDEO_STR "x-ms-wmv\0"
167    "wvx\0" VIDEO_STR "x-ms-wvx\0"
168    "xbm\0" IMAGE_STR "x-xbitmap\0"
169    "xml\0" TEXT_STR "xml\0"
170    "xsl\0" TEXT_STR "xml\0"
171    "z\0" APPLICATION_STR "x-compress\0"
172    "zip\0" APPLICATION_STR "zip\0" "\0";
173    // *** end ***
174
175/***********************************************************************/
176/*
177   module variables - Globals, static and externs
178 */
179
180/***********************************************************************/
181static struct document_type_t gMediaTypeList[NUM_MEDIA_TYPES];
182membuffer gDocumentRootDir;     // a local dir which serves as webserver root
183static struct xml_alias_t gAliasDoc;    // XML document
184static ithread_mutex_t gWebMutex;
185extern str_int_entry Http_Header_Names[NUM_HTTP_HEADER_NAMES];
186
187/************************************************************************
188* Function: has_xml_content_type
189*
190* Parameters:
191*	none
192*
193* Description: decodes list and stores it in gMediaTypeList
194*
195* Returns:
196*	 void
197************************************************************************/
198static XINLINE void
199media_list_init( void )
200{
201    int i;
202    const char *s = gEncodedMediaTypes;
203    struct document_type_t *doc_type;
204
205    for( i = 0; *s != '\0'; i++ ) {
206        doc_type = &gMediaTypeList[i];
207
208        doc_type->file_ext = s;
209
210        s += strlen( s ) + 1;   // point to type
211        doc_type->content_type = gMediaTypes[( int )*s];    // set cont-type
212
213        s++;                    // point to subtype
214        doc_type->content_subtype = s;
215
216        s += strlen( s ) + 1;   // next entry
217    }
218    assert( i == NUM_MEDIA_TYPES );
219}
220
221/************************************************************************
222* Function: has_xml_content_type
223*
224* Parameters:
225*	IN const char* extension ;
226*	OUT const char** con_type,
227*	OUT const char** con_subtype
228*
229* Description: Based on the extension, returns the content type and
230*	content subtype
231*
232* Returns:
233*	 0 on success;
234*	-1 on error
235************************************************************************/
236static XINLINE int
237search_extension( IN const char *extension,
238                  OUT const char **con_type,
239                  OUT const char **con_subtype )
240{
241    int top,
242      mid,
243      bot;
244    int cmp;
245
246    top = 0;
247    bot = NUM_MEDIA_TYPES - 1;
248
249    while( top <= bot ) {
250        mid = ( top + bot ) / 2;
251        cmp = strcasecmp( extension, gMediaTypeList[mid].file_ext );
252
253        if( cmp > 0 ) {
254            top = mid + 1;      // look below mid
255        } else if( cmp < 0 ) {
256            bot = mid - 1;      // look above mid
257        } else                  // cmp == 0
258        {
259            *con_type = gMediaTypeList[mid].content_type;
260            *con_subtype = gMediaTypeList[mid].content_subtype;
261            return 0;
262        }
263    }
264
265    return -1;
266}
267
268/************************************************************************
269* Function: get_content_type
270*
271* Parameters:
272*	IN const char* filename,
273*	OUT DOMString* content_type
274*
275* Description: Based on the extension, clones an XML string based on
276*	type and content subtype. If content type and sub type are not
277*	found, unknown types are used
278*
279* Returns:
280*	 0 - On Sucess
281*	 UPNP_E_OUTOF_MEMORY - on memory allocation failures
282************************************************************************/
283XINLINE int
284get_content_type( IN const char *filename,
285                  OUT DOMString * content_type )
286{
287    const char *extension;
288    const char *type,
289     *subtype;
290    xboolean ctype_found = FALSE;
291    char *temp = NULL;
292    int length = 0;
293
294    ( *content_type ) = NULL;
295
296    // get ext
297    extension = strrchr( filename, '.' );
298    if( extension != NULL ) {
299        if( search_extension( extension + 1, &type, &subtype ) == 0 ) {
300            ctype_found = TRUE;
301        }
302    }
303
304    if( !ctype_found ) {
305        // unknown content type
306        type = gMediaTypes[APPLICATION_INDEX];
307        subtype = "octet-stream";
308    }
309
310    length = strlen( type ) + strlen( "/" ) + strlen( subtype ) + 1;
311    temp = ( char * )malloc( length );
312
313    if( !temp ) {
314        return UPNP_E_OUTOF_MEMORY;
315    }
316
317    sprintf( temp, "%s/%s", type, subtype );
318    ( *content_type ) = ixmlCloneDOMString( temp );
319
320    free( temp );
321
322    if( !content_type ) {
323        return UPNP_E_OUTOF_MEMORY;
324    }
325
326    return 0;
327}
328
329/************************************************************************
330* Function: glob_alias_init
331*
332* Parameters:
333*	none
334*
335* Description: Initialize the global XML document. Allocate buffers
336*	for the XML document
337*
338* Returns:
339*	 void
340************************************************************************/
341static XINLINE void
342glob_alias_init( void )
343{
344    struct xml_alias_t *alias = &gAliasDoc;
345
346    membuffer_init( &alias->doc );
347    membuffer_init( &alias->name );
348    alias->ct = NULL;
349    alias->last_modified = 0;
350}
351
352/************************************************************************
353* Function: is_valid_alias
354*
355* Parameters:
356*	IN const struct xml_alias_t* alias ; XML alias object
357*
358* Description: Check for the validity of the XML object buffer
359*
360* Returns:
361*	 BOOLEAN
362************************************************************************/
363static XINLINE xboolean
364is_valid_alias( IN const struct xml_alias_t *alias )
365{
366    return alias->doc.buf != NULL;
367}
368
369/************************************************************************
370* Function: alias_grab
371*
372* Parameters:
373*	OUT struct xml_alias_t* alias ; XML alias object
374*
375* Description: Copy the contents of the global XML document into the
376*	local OUT parameter
377*
378* Returns:
379*	 void
380************************************************************************/
381static void
382alias_grab( OUT struct xml_alias_t *alias )
383{
384    ithread_mutex_lock( &gWebMutex );
385
386    assert( is_valid_alias( &gAliasDoc ) );
387
388    memcpy( alias, &gAliasDoc, sizeof( struct xml_alias_t ) );
389    *alias->ct = *alias->ct + 1;
390
391    ithread_mutex_unlock( &gWebMutex );
392}
393
394/************************************************************************
395* Function: alias_release
396*
397* Parameters:
398*	IN struct xml_alias_t* alias ; XML alias object
399*
400* Description: Release the XML document referred to by the IN parameter
401*	Free the allocated buffers associated with this object
402*
403* Returns:
404*	void
405************************************************************************/
406static void
407alias_release( IN struct xml_alias_t *alias )
408{
409    ithread_mutex_lock( &gWebMutex );
410
411    // ignore invalid alias
412    if( !is_valid_alias( alias ) ) {
413        ithread_mutex_unlock( &gWebMutex );
414        return;
415    }
416
417    assert( alias->ct > 0 );
418
419    *alias->ct = *alias->ct - 1;
420    if( *alias->ct <= 0 ) {
421        membuffer_destroy( &alias->doc );
422        membuffer_destroy( &alias->name );
423        free( alias->ct );
424    }
425    ithread_mutex_unlock( &gWebMutex );
426}
427
428/************************************************************************
429* Function: web_server_set_alias
430*
431* Parameters:
432*	alias_name: webserver name of alias; created by caller and freed by
433*				caller (doesn't even have to be malloc()d .)
434*	alias_content:	the xml doc; this is allocated by the caller; and
435*					freed by the web server
436*	alias_content_length: length of alias body in bytes
437*	last_modified:	time when the contents of alias were last
438*					changed (local time)
439*
440* Description: Replaces current alias with the given alias. To remove
441*	the current alias, set alias_name to NULL.
442*
443* Returns:
444*	0 - OK
445*	UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here
446************************************************************************/
447int
448web_server_set_alias( IN const char *alias_name,
449                      IN const char *alias_content,
450                      IN size_t alias_content_length,
451                      IN time_t last_modified )
452{
453    int ret_code;
454    struct xml_alias_t alias;
455
456    alias_release( &gAliasDoc );
457
458    if( alias_name == NULL ) {
459        // don't serve aliased doc anymore
460        return 0;
461    }
462
463    assert( alias_content != NULL );
464
465    membuffer_init( &alias.doc );
466    membuffer_init( &alias.name );
467    alias.ct = NULL;
468
469    do {
470        // insert leading /, if missing
471        if( *alias_name != '/' ) {
472            if( membuffer_assign_str( &alias.name, "/" ) != 0 ) {
473                break;          // error; out of mem
474            }
475        }
476
477        ret_code = membuffer_append_str( &alias.name, alias_name );
478        if( ret_code != 0 ) {
479            break;              // error
480        }
481
482        if( ( alias.ct = ( int * )malloc( sizeof( int ) ) ) == NULL ) {
483            break;              // error
484        }
485        *alias.ct = 1;
486        membuffer_attach( &alias.doc, ( char * )alias_content,
487                          alias_content_length );
488
489        alias.last_modified = last_modified;
490
491        // save in module var
492        ithread_mutex_lock( &gWebMutex );
493        gAliasDoc = alias;
494        ithread_mutex_unlock( &gWebMutex );
495
496        return 0;
497    } while( FALSE );
498
499    // error handler
500
501    // free temp alias
502    membuffer_destroy( &alias.name );
503    membuffer_destroy( &alias.doc );
504    free( alias.ct );
505    return UPNP_E_OUTOF_MEMORY;
506}
507
508/************************************************************************
509* Function: web_server_init
510*
511* Parameters:
512*	none
513*
514* Description: Initilialize the different documents. Initialize the
515*	memory for root directory for web server. Call to initialize global
516*	XML document. Sets bWebServerState to WEB_SERVER_ENABLED
517*
518* Returns:
519*	0 - OK
520*	UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here
521************************************************************************/
522int
523web_server_init( void )
524{
525    int ret_code;
526
527    if( bWebServerState == WEB_SERVER_DISABLED ) {
528        media_list_init(  );    // decode media list
529        membuffer_init( &gDocumentRootDir );
530        glob_alias_init(  );
531
532        pVirtualDirList = NULL;
533
534        ret_code = ithread_mutex_init( &gWebMutex, NULL );
535        if( ret_code == -1 ) {
536            return UPNP_E_OUTOF_MEMORY;
537        }
538        bWebServerState = WEB_SERVER_ENABLED;
539    }
540
541    return 0;
542}
543
544/************************************************************************
545* Function: web_server_destroy
546*
547* Parameters:
548*	none
549*
550* Description: Release memory allocated for the global web server root
551*	directory and the global XML document
552*	Resets the flag bWebServerState to WEB_SERVER_DISABLED
553*
554* Returns:
555*	void
556************************************************************************/
557void
558web_server_destroy( void )
559{
560    int ret;
561
562    if( bWebServerState == WEB_SERVER_ENABLED ) {
563        membuffer_destroy( &gDocumentRootDir );
564        alias_release( &gAliasDoc );
565
566        ithread_mutex_lock( &gWebMutex );
567        memset( &gAliasDoc, 0, sizeof( struct xml_alias_t ) );
568        ithread_mutex_unlock( &gWebMutex );
569
570        ret = ithread_mutex_destroy( &gWebMutex );
571        assert( ret == 0 );
572        bWebServerState = WEB_SERVER_DISABLED;
573    }
574}
575
576/************************************************************************
577* Function: get_file_info
578*
579* Parameters:
580*	IN const char* filename ; 	Filename having the description document
581*	OUT struct File_Info * info ; File information object having file
582*								  attributes such as filelength, when was
583*								  the file last modified, whether a file
584*								  or a directory and whether the file or
585*								  directory is readable.
586*
587* Description: Release memory allocated for the global web server root
588*	directory and the global XML document
589*	Resets the flag bWebServerState to WEB_SERVER_DISABLED
590*
591* Returns:
592*	int
593************************************************************************/
594static int
595get_file_info( IN const char *filename,
596               OUT struct File_Info *info )
597{
598    int code;
599    struct stat s;
600    FILE *fp;
601    int rc = 0;
602
603    info->content_type = NULL;
604
605    code = stat( filename, &s );
606    if( code == -1 ) {
607        return -1;
608    }
609
610    if( S_ISDIR( s.st_mode ) ) {
611        info->is_directory = TRUE;
612    } else if( S_ISREG( s.st_mode ) ) {
613        info->is_directory = FALSE;
614    } else {
615        return -1;
616    }
617
618    // check readable
619    fp = fopen( filename, "r" );
620    info->is_readable = ( fp != NULL );
621    if( fp ) {
622        fclose( fp );
623    }
624
625    info->file_length = s.st_size;
626    info->last_modified = s.st_mtime;
627
628    rc = get_content_type( filename, &info->content_type );
629
630    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
631                         "file info: %s, length: %d, last_mod=%s readable=%d\n",
632                         filename, info->file_length,
633                         asctime( gmtime( &info->last_modified ) ),
634                         info->is_readable ); )
635
636        return rc;
637}
638
639/************************************************************************
640* Function: web_server_set_root_dir
641*
642* Parameters:
643*	IN const char* root_dir ; String having the root directory for the
644*								document
645*
646* Description: Assign the path specfied by the IN const char* root_dir
647*	parameter to the global Document root directory. Also check for
648*	path names ending in '/'
649*
650* Returns:
651*	int
652************************************************************************/
653int
654web_server_set_root_dir( IN const char *root_dir )
655{
656    int index_1;
657    int ret;
658
659    ret = membuffer_assign_str( &gDocumentRootDir, root_dir );
660    if( ret != 0 ) {
661        return ret;
662    }
663    // remove trailing '/', if any
664    if( gDocumentRootDir.length > 0 ) {
665        index_1 = gDocumentRootDir.length - 1;    // last char
666        if( gDocumentRootDir.buf[index_1] == '/' ) {
667            membuffer_delete( &gDocumentRootDir, index_1, 1 );
668        }
669    }
670
671    return 0;
672}
673
674/************************************************************************
675* Function: get_alias
676*
677* Parameters:
678*	IN const char* request_file ; request file passed in to be compared with
679*	OUT struct xml_alias_t* alias ; xml alias object which has a file name
680*									stored
681*   OUT struct File_Info * info	 ; File information object which will be
682*									filled up if the file comparison
683*									succeeds
684*
685* Description: Compare the files names between the one on the XML alias
686*	the one passed in as the input parameter. If equal extract file
687*	information
688*
689* Returns:
690*	TRUE - On Success
691*	FALSE if request is not an alias
692************************************************************************/
693static XINLINE xboolean
694get_alias( IN const char *request_file,
695           OUT struct xml_alias_t *alias,
696           OUT struct File_Info *info )
697{
698    int cmp;
699
700    cmp = strcmp( alias->name.buf, request_file );
701    if( cmp == 0 ) {
702        // fill up info
703        info->file_length = alias->doc.length;
704        info->is_readable = TRUE;
705        info->is_directory = FALSE;
706        info->last_modified = alias->last_modified;
707    }
708
709    return cmp == 0;
710}
711
712/************************************************************************
713* Function: isFileInVirtualDir
714*
715* Parameters:
716*	IN char *filePath ; directory path to be tested for virtual directory
717*
718* Description: Compares filePath with paths from the list of virtual
719*				directory lists
720*
721* Returns:
722*	BOOLEAN
723************************************************************************/
724int
725isFileInVirtualDir( IN char *filePath )
726{
727    virtualDirList *pCurVirtualDir;
728    int webDirLen;
729
730    pCurVirtualDir = pVirtualDirList;
731    while( pCurVirtualDir != NULL ) {
732        webDirLen = strlen( pCurVirtualDir->dirName );
733        if( pCurVirtualDir->dirName[webDirLen - 1] == '/' ) {
734            if( strncmp( pCurVirtualDir->dirName, filePath, webDirLen ) ==
735                0 )
736                return TRUE;
737        } else {
738            if( ( strncmp( pCurVirtualDir->dirName, filePath, webDirLen )
739                  == 0 ) && ( filePath[webDirLen] == '/' ) )
740                return TRUE;
741        }
742
743        pCurVirtualDir = pCurVirtualDir->next;
744    }
745
746    return FALSE;
747}
748
749/************************************************************************
750* Function: ToUpperCase
751*
752* Parameters:
753*	INOUT char * Str ; Input string to be converted
754*
755* Description: Converts input string to upper case
756*
757* Returns:
758*	int
759************************************************************************/
760int
761ToUpperCase( char *Str )
762{
763    int i;
764
765    for( i = 0; i < ( int )strlen( Str ); i++ )
766        Str[i] = toupper( Str[i] );
767    return 1;
768
769}
770
771/************************************************************************
772* Function: StrStr
773*
774* Parameters:
775*	IN char * S1 ; Input string
776*	IN char * S2 ; Input sub-string
777*
778* Description: Finds a substring from a string
779*
780* Returns:
781*	char * ptr - pointer to the first occurence of S2 in S1
782************************************************************************/
783char *
784StrStr( char *S1,
785        char *S2 )
786{
787    char *Str1,
788     *Str2;
789    char *Ptr,
790     *Ret;
791    int Pos;
792
793    Str1 = ( char * )malloc( strlen( S1 ) + 2 );
794    if(!Str1)
795    	return NULL;
796    Str2 = ( char * )malloc( strlen( S2 ) + 2 );
797    if( !Str2 ){
798    	free(Str1);
799        return NULL;
800	}
801    strcpy( Str1, S1 );
802    strcpy( Str2, S2 );
803
804    ToUpperCase( Str1 );
805    ToUpperCase( Str2 );
806    Ptr = strstr( Str1, Str2 );
807    if( Ptr == NULL )
808        return NULL;
809
810    Pos = Ptr - Str1;
811
812    Ret = S1 + Pos;
813
814    free( Str1 );
815    free( Str2 );
816    return Ret;
817
818}
819
820/************************************************************************
821* Function: StrTok
822*
823* Parameters:
824*	IN char ** Src ; String containing the token
825*	IN char * del ; Set of delimiter characters
826*
827* Description: Finds next token in a string
828*
829* Returns:
830*	char * ptr - pointer to the first occurence of S2 in S1
831************************************************************************/
832char *
833StrTok( char **Src,
834        char *Del )
835{
836    char *TmpPtr,
837     *RetPtr;
838
839    if( *Src != NULL ) {
840        RetPtr = *Src;
841        TmpPtr = strstr( *Src, Del );
842        if( TmpPtr != NULL ) {
843            *TmpPtr = '\0';
844            *Src = TmpPtr + strlen( Del );
845        } else
846            *Src = NULL;
847
848        return RetPtr;
849    }
850
851    return NULL;
852}
853
854/************************************************************************
855* Function: GetNextRange
856*
857* Parameters:
858*	IN char ** SrcRangeStr ; string containing the token / range
859*	OUT int * FirstByte ;	 gets the first byte of the token
860*	OUT int * LastByte	; gets the last byte of the token
861*
862* Description: Returns a range of integers from a sring
863*
864* Returns: int	;
865*	always returns 1;
866************************************************************************/
867int
868GetNextRange( char **SrcRangeStr,
869              int *FirstByte,
870              int *LastByte )
871{
872    char *Ptr,
873     *Tok;
874    int i,
875      F = -1,
876      L = -1;
877    int Is_Suffix_byte_Range = 1;
878
879    if( *SrcRangeStr == NULL )
880        return -1;
881
882    Tok = StrTok( SrcRangeStr, "," );
883
884    if( ( Ptr = strstr( Tok, "-" ) ) == NULL )
885        return -1;
886    *Ptr = ' ';
887    sscanf( Tok, "%d%d", &F, &L );
888
889    if( F == -1 || L == -1 ) {
890        *Ptr = '-';
891        for( i = 0; i < ( int )strlen( Tok ); i++ ) {
892            if( Tok[i] == '-' ) {
893                break;
894            } else if( isdigit( Tok[i] ) ) {
895                Is_Suffix_byte_Range = 0;
896                break;
897            }
898
899        }
900
901        if( Is_Suffix_byte_Range ) {
902            *FirstByte = L;
903            *LastByte = F;
904            return 1;
905        }
906    }
907
908    *FirstByte = F;
909    *LastByte = L;
910    return 1;
911
912}
913
914/************************************************************************
915* Function: CreateHTTPRangeResponseHeader
916*
917* Parameters:
918*	char * ByteRangeSpecifier ; String containing the range
919*	long FileLength ; Length of the file
920*	OUT struct SendInstruction * Instr ; SendInstruction object	where the
921*										range operations will be stored
922*
923* Description: Fills in the Offset, read size and contents to send out
924*	as an HTTP Range Response
925*
926* Returns:
927*	HTTP_BAD_REQUEST
928*	UPNP_E_OUTOF_MEMORY
929*	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
930*	HTTP_OK
931************************************************************************/
932int
933CreateHTTPRangeResponseHeader( char *ByteRangeSpecifier,
934                               long FileLength,
935                               OUT struct SendInstruction *Instr )
936{
937
938    int FirstByte,
939      LastByte;
940    char *RangeInput,
941     *Ptr;
942
943    Instr->IsRangeActive = 1;
944    Instr->ReadSendSize = FileLength;
945
946    if( !ByteRangeSpecifier )
947        return HTTP_BAD_REQUEST;
948
949    RangeInput = malloc( strlen( ByteRangeSpecifier ) + 1 );
950    if( !RangeInput )
951        return UPNP_E_OUTOF_MEMORY;
952    strcpy( RangeInput, ByteRangeSpecifier );
953
954    //CONTENT-RANGE: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
955    if( StrStr( RangeInput, "bytes" ) == NULL ||
956        ( Ptr = StrStr( RangeInput, "=" ) ) == NULL ) {
957        free( RangeInput );
958        Instr->IsRangeActive = 0;
959        return HTTP_BAD_REQUEST;
960    }
961    //Jump =
962    Ptr = Ptr + 1;
963
964    if( FileLength < 0 ) {
965        free( RangeInput );
966        return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
967    }
968
969    if( GetNextRange( &Ptr, &FirstByte, &LastByte ) != -1 ) {
970
971        if( FileLength < FirstByte ) {
972            free( RangeInput );
973            return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
974        }
975
976        if( FirstByte >= 0 && LastByte >= 0 && LastByte >= FirstByte ) {
977            if( LastByte >= FileLength )
978                LastByte = FileLength - 1;
979
980            Instr->RangeOffset = FirstByte;
981            Instr->ReadSendSize = LastByte - FirstByte + 1;
982            sprintf( Instr->RangeHeader, "CONTENT-RANGE: bytes %d-%d/%ld\r\n", FirstByte, LastByte, FileLength );   //Data between two range.
983        } else if( FirstByte >= 0 && LastByte == -1
984                   && FirstByte < FileLength ) {
985            Instr->RangeOffset = FirstByte;
986            Instr->ReadSendSize = FileLength - FirstByte;
987            sprintf( Instr->RangeHeader,
988                     "CONTENT-RANGE: bytes %d-%ld/%ld\r\n", FirstByte,
989                     FileLength - 1, FileLength );
990        } else if( FirstByte == -1 && LastByte > 0 ) {
991            if( LastByte >= FileLength ) {
992                Instr->RangeOffset = 0;
993                Instr->ReadSendSize = FileLength;
994                sprintf( Instr->RangeHeader,
995                         "CONTENT-RANGE: bytes 0-%ld/%ld\r\n",
996                         FileLength - 1, FileLength );
997            } else {
998                Instr->RangeOffset = FileLength - LastByte;
999                Instr->ReadSendSize = LastByte;
1000                sprintf( Instr->RangeHeader,
1001                         "CONTENT-RANGE: bytes %ld-%ld/%ld\r\n",
1002                         FileLength - LastByte + 1, FileLength,
1003                         FileLength );
1004            }
1005        } else {
1006            free( RangeInput );
1007            return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
1008        }
1009    } else {
1010        free( RangeInput );
1011        return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
1012    }
1013
1014    free( RangeInput );
1015    return HTTP_OK;
1016}
1017
1018/************************************************************************
1019* Function: CheckOtherHTTPHeaders
1020*
1021* Parameters:
1022*	IN http_message_t * Req ;  HTTP Request message
1023*	OUT struct SendInstruction * RespInstr ; Send Instruction object to
1024*							data for the response
1025*	int FileSize ;	Size of the file containing the request document
1026*
1027* Description: Get header id from the request parameter and take
1028*	appropriate action based on the ids.
1029*	as an HTTP Range Response
1030*
1031* Returns:
1032*	HTTP_BAD_REQUEST
1033*	UPNP_E_OUTOF_MEMORY
1034*	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
1035*	HTTP_OK
1036************************************************************************/
1037int
1038CheckOtherHTTPHeaders( IN http_message_t * Req,
1039                       OUT struct SendInstruction *RespInstr,
1040                       int FileSize )
1041{
1042    http_header_t *header;
1043    ListNode *node;
1044
1045    //NNS: dlist_node* node;
1046    int index_1,
1047      RetCode = HTTP_OK;
1048    char *TmpBuf;
1049
1050    TmpBuf = ( char * )malloc( LINE_SIZE );
1051    if( !TmpBuf )
1052        return UPNP_E_OUTOF_MEMORY;
1053
1054    node = ListHead( &Req->headers );
1055
1056    while( node != NULL ) {
1057        header = ( http_header_t * ) node->item;
1058
1059        // find header type.
1060        index_1 = map_str_to_int( ( const char * )header->name.buf,
1061                                header->name.length, Http_Header_Names,
1062                                NUM_HTTP_HEADER_NAMES, FALSE );
1063
1064        if( header->value.length >= LINE_SIZE ) {
1065            free( TmpBuf );
1066            TmpBuf = ( char * )malloc( header->value.length + 1 );
1067            if( !TmpBuf )
1068                return UPNP_E_OUTOF_MEMORY;
1069        }
1070
1071        memcpy( TmpBuf, header->value.buf, header->value.length );
1072        TmpBuf[header->value.length] = '\0';
1073        if( index_1 >= 0 )
1074            switch ( Http_Header_Names[index_1].id ) {
1075                case HDR_TE:   //Request
1076                    {
1077                        RespInstr->IsChunkActive = 1;
1078
1079                        if( strlen( TmpBuf ) > strlen( "gzip" ) ) {
1080                            if( StrStr( TmpBuf, "trailers" ) != NULL ) {    //means client will accept trailer
1081                                RespInstr->IsTrailers = 1;
1082                            }
1083                        }
1084                    }
1085                    break;
1086
1087                case HDR_CONTENT_LENGTH:
1088                    {
1089                        RespInstr->RecvWriteSize = atoi( TmpBuf );
1090                        break;
1091                    }
1092
1093                case HDR_RANGE:
1094                    if( ( RetCode = CreateHTTPRangeResponseHeader( TmpBuf,
1095                                                                   FileSize,
1096                                                                   RespInstr ) )
1097                        != HTTP_OK ) {
1098                        free( TmpBuf );
1099                        return RetCode;
1100                    }
1101                    break;
1102                default:
1103                    /*
1104                       TODO
1105                     */
1106                    /*
1107                       header.value is the value.
1108                     */
1109                    /*
1110                       case HDR_CONTENT_TYPE: //return 1;
1111                       case HDR_CONTENT_LANGUAGE://return 1;
1112                       case HDR_LOCATION: //return 1;
1113                       case HDR_CONTENT_LOCATION://return 1;
1114                       case HDR_ACCEPT: //return 1;
1115                       case HDR_ACCEPT_CHARSET://return 1;
1116                       case HDR_ACCEPT_LANGUAGE://return 1;
1117                       case HDR_USER_AGENT: break;//return 1;
1118                     */
1119
1120                    //Header check for encoding
1121                    /*
1122                       case HDR_ACCEPT_RANGE: //Server capability.
1123                       case HDR_CONTENT_RANGE://Response.
1124                       case HDR_IF_RANGE:
1125                     */
1126
1127                    //Header check for encoding
1128                    /*
1129                       case HDR_ACCEPT_ENCODING:
1130                       if(StrStr(TmpBuf, "identity"))
1131                       {
1132                       break;
1133                       }
1134                       else return -1;
1135                       case HDR_CONTENT_ENCODING:
1136                       case HDR_TRANSFER_ENCODING: //Response
1137                     */
1138                    break;
1139            }
1140
1141        node = ListNext( &Req->headers, node );
1142
1143    }
1144
1145    free( TmpBuf );
1146    return RetCode;
1147}
1148
1149/************************************************************************
1150* Function: process_request
1151*
1152* Parameters:
1153*	IN http_message_t *req ; HTTP Request message
1154*	OUT enum resp_type *rtype ; Tpye of response
1155*	OUT membuffer *headers ;
1156*	OUT membuffer *filename ; Get filename from request document
1157*	OUT struct xml_alias_t *alias ; Xml alias document from the
1158*									request document,
1159*	OUT struct SendInstruction * RespInstr ; Send Instruction object
1160*					where the response is set up.
1161*
1162* Description: Processes the request and returns the result in the OUT
1163*	parameters
1164*
1165* Returns:
1166*	HTTP_BAD_REQUEST
1167*	UPNP_E_OUTOF_MEMORY
1168*	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
1169*	HTTP_OK
1170************************************************************************/
1171static int
1172process_request( IN http_message_t * req,
1173                 OUT enum resp_type *rtype,
1174                 OUT membuffer * headers,
1175                 OUT membuffer * filename,
1176                 OUT struct xml_alias_t *alias,
1177                 OUT struct SendInstruction *RespInstr )
1178{
1179    int code;
1180    int err_code;
1181
1182    //membuffer content_type;
1183    char *request_doc;
1184    struct File_Info finfo;
1185    xboolean using_alias;
1186    xboolean using_virtual_dir;
1187    uri_type *url;
1188    char *temp_str;
1189    int resp_major,
1190      resp_minor;
1191    xboolean alias_grabbed;
1192    int dummy;
1193    struct UpnpVirtualDirCallbacks *pVirtualDirCallback;
1194
1195    print_http_headers( req );
1196
1197    url = &req->uri;
1198    assert( req->method == HTTPMETHOD_GET ||
1199            req->method == HTTPMETHOD_HEAD
1200            || req->method == HTTPMETHOD_POST
1201            || req->method == HTTPMETHOD_SIMPLEGET );
1202
1203    // init
1204    request_doc = NULL;
1205    finfo.content_type = NULL;
1206    //membuffer_init( &content_type );
1207    alias_grabbed = FALSE;
1208    err_code = HTTP_INTERNAL_SERVER_ERROR;  // default error
1209    using_virtual_dir = FALSE;
1210    using_alias = FALSE;
1211
1212    http_CalcResponseVersion( req->major_version, req->minor_version,
1213                              &resp_major, &resp_minor );
1214
1215    //
1216    // remove dots
1217    //
1218    request_doc = malloc( url->pathquery.size + 1 );
1219    if( request_doc == NULL ) {
1220        goto error_handler;     // out of mem
1221    }
1222    memcpy( request_doc, url->pathquery.buff, url->pathquery.size );
1223    request_doc[url->pathquery.size] = '\0';
1224    dummy = url->pathquery.size;
1225    remove_escaped_chars( request_doc, &dummy );
1226    code = remove_dots( request_doc, url->pathquery.size );
1227    if( code != 0 ) {
1228        err_code = HTTP_FORBIDDEN;
1229        goto error_handler;
1230    }
1231
1232    if( *request_doc != '/' ) {
1233        // no slash
1234        err_code = HTTP_BAD_REQUEST;
1235        goto error_handler;
1236    }
1237
1238    if( isFileInVirtualDir( request_doc ) ) {
1239        using_virtual_dir = TRUE;
1240        RespInstr->IsVirtualFile = 1;
1241        if( membuffer_assign_str( filename, request_doc ) != 0 ) {
1242            goto error_handler;
1243        }
1244
1245    } else {
1246        //
1247        // try using alias
1248        //
1249        if( is_valid_alias( &gAliasDoc ) ) {
1250            alias_grab( alias );
1251            alias_grabbed = TRUE;
1252
1253            using_alias = get_alias( request_doc, alias, &finfo );
1254            if( using_alias == TRUE ) {
1255                finfo.content_type = ixmlCloneDOMString( "text/xml" );
1256
1257                if( finfo.content_type == NULL ) {
1258                    goto error_handler;
1259                }
1260            }
1261        }
1262    }
1263
1264    if( using_virtual_dir ) {
1265        if( req->method != HTTPMETHOD_POST ) {
1266            // get file info
1267            pVirtualDirCallback = &virtualDirCallback;
1268            if( pVirtualDirCallback->get_info( filename->buf, &finfo ) !=
1269                0 ) {
1270                err_code = HTTP_NOT_FOUND;
1271                goto error_handler;
1272            }
1273            // try index.html if req is a dir
1274            if( finfo.is_directory ) {
1275                if( filename->buf[filename->length - 1] == '/' ) {
1276                    temp_str = "index.html";
1277                } else {
1278                    temp_str = "/index.html";
1279                }
1280                if( membuffer_append_str( filename, temp_str ) != 0 ) {
1281                    goto error_handler;
1282                }
1283                // get info
1284                if( ( pVirtualDirCallback->
1285                      get_info( filename->buf, &finfo ) != UPNP_E_SUCCESS )
1286                    || finfo.is_directory ) {
1287                    err_code = HTTP_NOT_FOUND;
1288                    goto error_handler;
1289                }
1290            }
1291            // not readable
1292            if( !finfo.is_readable ) {
1293                err_code = HTTP_FORBIDDEN;
1294                goto error_handler;
1295            }
1296            // finally, get content type
1297            // if ( get_content_type(filename->buf, &content_type) != 0 )
1298            //{
1299            //  goto error_handler;
1300            // }
1301        }
1302    } else if( !using_alias ) {
1303        if( gDocumentRootDir.length == 0 ) {
1304            goto error_handler;
1305        }
1306        //
1307        // get file name
1308        //
1309
1310        // filename str
1311        if( membuffer_assign_str( filename, gDocumentRootDir.buf ) != 0 ||
1312            membuffer_append_str( filename, request_doc ) != 0 ) {
1313            goto error_handler; // out of mem
1314        }
1315        // remove trailing slashes
1316        while( filename->length > 0 &&
1317               filename->buf[filename->length - 1] == '/' ) {
1318            membuffer_delete( filename, filename->length - 1, 1 );
1319        }
1320
1321        if( req->method != HTTPMETHOD_POST ) {
1322            // get info on file
1323            if( get_file_info( filename->buf, &finfo ) != 0 ) {
1324                err_code = HTTP_NOT_FOUND;
1325                goto error_handler;
1326            }
1327            // try index.html if req is a dir
1328            if( finfo.is_directory ) {
1329                if( filename->buf[filename->length - 1] == '/' ) {
1330                    temp_str = "index.html";
1331                } else {
1332                    temp_str = "/index.html";
1333                }
1334                if( membuffer_append_str( filename, temp_str ) != 0 ) {
1335                    goto error_handler;
1336                }
1337                // get info
1338                if( get_file_info( filename->buf, &finfo ) != 0 ||
1339                    finfo.is_directory ) {
1340                    err_code = HTTP_NOT_FOUND;
1341                    goto error_handler;
1342                }
1343            }
1344            // not readable
1345            if( !finfo.is_readable ) {
1346                err_code = HTTP_FORBIDDEN;
1347                goto error_handler;
1348            }
1349
1350        }
1351        // finally, get content type
1352        //      if ( get_content_type(filename->buf, &content_type) != 0 )
1353        //      {
1354        //          goto error_handler;
1355        //      }
1356    }
1357
1358    RespInstr->ReadSendSize = finfo.file_length;
1359
1360    //Check other header field.
1361    if( ( err_code =
1362          CheckOtherHTTPHeaders( req, RespInstr,
1363                                 finfo.file_length ) ) != HTTP_OK ) {
1364        goto error_handler;
1365    }
1366
1367    if( req->method == HTTPMETHOD_POST ) {
1368        *rtype = RESP_POST;
1369        err_code = UPNP_E_SUCCESS;
1370        goto error_handler;
1371    }
1372
1373    if( RespInstr->IsRangeActive && RespInstr->IsChunkActive ) {
1374        //Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
1375        //Transfer-Encoding: chunked
1376        // K means add chunky header ang G means range header.
1377        if( http_MakeMessage( headers, resp_major, resp_minor, "RTGKDstcSCc", HTTP_PARTIAL_CONTENT, // status code
1378                              // RespInstr->ReadSendSize,// content length
1379                              finfo.content_type,
1380                              //     content_type.buf,            // content type
1381                              RespInstr,    // Range
1382                              "LAST-MODIFIED: ",
1383                              &finfo.last_modified ) != 0 ) {
1384            goto error_handler;
1385        }
1386    } else if( RespInstr->IsRangeActive && !RespInstr->IsChunkActive ) {
1387
1388        //Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
1389        //Transfer-Encoding: chunked
1390        // K means add chunky header ang G means range header.
1391        if( http_MakeMessage( headers, resp_major, resp_minor, "RNTGDstcSCc", HTTP_PARTIAL_CONTENT, // status code
1392                              RespInstr->ReadSendSize,  // content length
1393                              finfo.content_type,
1394                              //content_type.buf,            // content type
1395                              RespInstr,    //Range Info
1396                              "LAST-MODIFIED: ",
1397                              &finfo.last_modified ) != 0 ) {
1398            goto error_handler;
1399        }
1400
1401    } else if( !RespInstr->IsRangeActive && RespInstr->IsChunkActive ) {
1402
1403        //Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
1404        //Transfer-Encoding: chunked
1405        // K means add chunky header ang G means range header.
1406        if( http_MakeMessage( headers, resp_major, resp_minor, "RKTDstcSCc", HTTP_OK,   // status code
1407                              //RespInstr->ReadSendSize,// content length
1408                              finfo.content_type,
1409                              // content_type.buf,            // content type
1410                              "LAST-MODIFIED: ",
1411                              &finfo.last_modified ) != 0 ) {
1412            goto error_handler;
1413        }
1414
1415    } else {
1416        if( RespInstr->ReadSendSize >= 0 ) {
1417            //Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
1418            //Transfer-Encoding: chunked
1419            // K means add chunky header ang G means range header.
1420            if( http_MakeMessage( headers, resp_major, resp_minor, "RNTDstcSCc", HTTP_OK,   // status code
1421                                  RespInstr->ReadSendSize,  // content length
1422                                  finfo.content_type,
1423                                  //content_type.buf,          // content type
1424                                  "LAST-MODIFIED: ",
1425                                  &finfo.last_modified ) != 0 ) {
1426                goto error_handler;
1427            }
1428        } else {
1429            //Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
1430            //Transfer-Encoding: chunked
1431            // K means add chunky header ang G means range header.
1432            if( http_MakeMessage( headers, resp_major, resp_minor, "RTDstcSCc", HTTP_OK,    // status code
1433                                  //RespInstr->ReadSendSize,// content length
1434                                  finfo.content_type,
1435                                  //content_type.buf,          // content type
1436                                  "LAST-MODIFIED: ",
1437                                  &finfo.last_modified ) != 0 ) {
1438                goto error_handler;
1439            }
1440        }
1441    }
1442
1443    if( req->method == HTTPMETHOD_HEAD ) {
1444        *rtype = RESP_HEADERS;
1445    } else if( using_alias ) {
1446        // GET xml
1447        *rtype = RESP_XMLDOC;
1448    } else if( using_virtual_dir ) {
1449        *rtype = RESP_WEBDOC;
1450    } else {
1451        // GET filename
1452        *rtype = RESP_FILEDOC;
1453    }
1454
1455    //simple get http 0.9 as specified in http 1.0
1456    //don't send headers
1457    if( req->method == HTTPMETHOD_SIMPLEGET ) {
1458        membuffer_destroy( headers );
1459    }
1460
1461    err_code = UPNP_E_SUCCESS;
1462
1463  error_handler:
1464    free( request_doc );
1465    ixmlFreeDOMString( finfo.content_type );
1466    //  membuffer_destroy( &content_type );
1467    if( err_code != UPNP_E_SUCCESS && alias_grabbed ) {
1468        alias_release( alias );
1469    }
1470
1471    return err_code;
1472}
1473
1474/************************************************************************
1475* Function: http_RecvPostMessage
1476*
1477* Parameters:
1478*	http_parser_t* parser ; HTTP Parser object
1479*	IN SOCKINFO *info ; Socket Information object
1480*	char * filename ; 	File where received data is copied to
1481*	struct SendInstruction * Instr	; Send Instruction object which gives
1482*			information whether the file is a virtual file or not.
1483*
1484* Description: Receives the HTTP post message
1485*
1486* Returns:
1487*	HTTP_INTERNAL_SERVER_ERROR
1488*	HTTP_UNAUTHORIZED
1489*	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
1490*	HTTP_OK
1491************************************************************************/
1492int
1493http_RecvPostMessage( http_parser_t * parser,
1494                      IN SOCKINFO * info,
1495                      char *filename,
1496                      struct SendInstruction *Instr )
1497{
1498
1499    unsigned int Data_Buf_Size = 512;
1500    static char Buf[512];
1501    int Timeout = 0;
1502    long Num_Write = 0;
1503    FILE *Fp;
1504    parse_status_t status = PARSE_OK;
1505    xboolean ok_on_close = FALSE;
1506    unsigned int entity_offset = 0;
1507    int num_read = 0;
1508    int ret_code = 0;
1509
1510    if( Instr && Instr->IsVirtualFile ) {
1511
1512        Fp = virtualDirCallback.open( filename, UPNP_WRITE );
1513        if( Fp == NULL ) {
1514            return HTTP_INTERNAL_SERVER_ERROR;
1515        }
1516
1517    } else {
1518        Fp = fopen( filename, "wb" );
1519        if( Fp == NULL ) {
1520            return HTTP_UNAUTHORIZED;
1521        }
1522    }
1523
1524    parser->position = POS_ENTITY;
1525
1526    do {
1527        //first parse what has already been gotten
1528        if( parser->position != POS_COMPLETE ) {
1529            status = parser_parse_entity( parser );
1530        }
1531
1532        if( status == PARSE_INCOMPLETE_ENTITY ) {
1533            // read until close
1534            ok_on_close = TRUE;
1535        } else if( ( status != PARSE_SUCCESS )
1536                   && ( status != PARSE_CONTINUE_1 )
1537                   && ( status != PARSE_INCOMPLETE ) ) {
1538            //error
1539            return HTTP_BAD_REQUEST;
1540        }
1541        //read more if necessary entity
1542        while( ( ( entity_offset + Data_Buf_Size ) >
1543                 parser->msg.entity.length )
1544               && ( parser->position != POS_COMPLETE ) ) {
1545            num_read = sock_read( info, Buf, sizeof( Buf ), &Timeout );
1546            if( num_read > 0 ) {
1547                // append data to buffer
1548                ret_code = membuffer_append( &parser->msg.msg,
1549                                             Buf, num_read );
1550                if( ret_code != 0 ) {
1551                    // set failure status
1552                    parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1553                    return HTTP_INTERNAL_SERVER_ERROR;
1554                }
1555                status = parser_parse_entity( parser );
1556                if( status == PARSE_INCOMPLETE_ENTITY ) {
1557                    // read until close
1558                    ok_on_close = TRUE;
1559                } else if( ( status != PARSE_SUCCESS )
1560                           && ( status != PARSE_CONTINUE_1 )
1561                           && ( status != PARSE_INCOMPLETE ) ) {
1562                    return HTTP_BAD_REQUEST;
1563                }
1564            } else if( num_read == 0 ) {
1565                if( ok_on_close ) {
1566                    DBGONLY( UpnpPrintf
1567                             ( UPNP_INFO, HTTP, __FILE__, __LINE__,
1568                               "<<< (RECVD) <<<\n%s\n-----------------\n",
1569                               parser->msg.msg.buf );
1570                             //print_http_headers( &parser->msg );
1571                         )
1572
1573                        parser->position = POS_COMPLETE;
1574                } else {
1575                    // partial msg
1576                    parser->http_error_code = HTTP_BAD_REQUEST; // or response
1577                    return HTTP_BAD_REQUEST;
1578                }
1579            } else {
1580                return num_read;
1581            }
1582        }
1583
1584        if( ( entity_offset + Data_Buf_Size ) > parser->msg.entity.length ) {
1585            Data_Buf_Size = parser->msg.entity.length - entity_offset;
1586        }
1587
1588        memcpy( Buf, &parser->msg.msg.buf[parser->entity_start_position
1589                                          + entity_offset],
1590                Data_Buf_Size );
1591        entity_offset += Data_Buf_Size;
1592
1593        if( Instr->IsVirtualFile ) {
1594            Num_Write = virtualDirCallback.write( Fp, Buf, Data_Buf_Size );
1595            if( Num_Write < 0 ) {
1596                virtualDirCallback.close( Fp );
1597                return HTTP_INTERNAL_SERVER_ERROR;
1598            }
1599        } else {
1600            Num_Write = fwrite( Buf, 1, Data_Buf_Size, Fp );
1601            if( Num_Write < 0 ) {
1602                fclose( Fp );
1603                return HTTP_INTERNAL_SERVER_ERROR;
1604            }
1605        }
1606
1607    } while( ( parser->position != POS_COMPLETE )
1608             || ( entity_offset != parser->msg.entity.length ) );
1609
1610    if( Instr->IsVirtualFile ) {
1611        virtualDirCallback.close( Fp );
1612    } else {
1613        fclose( Fp );
1614    }
1615
1616    /*
1617       while(TotalByteReceived < Instr->RecvWriteSize &&
1618       (NumReceived = sock_read(info,Buf, Data_Buf_Size,&Timeout) ) > 0 )
1619       {
1620       TotalByteReceived = TotalByteReceived + NumReceived;
1621       Num_Write = virtualDirCallback.write(Fp, Buf, NumReceived);
1622       if (ferror(Fp))
1623       {
1624       virtualDirCallback.close(Fp);
1625       return HTTP_INTERNAL_SERVER_ERROR;
1626       }
1627       }
1628
1629       if(TotalByteReceived < Instr->RecvWriteSize)
1630       {
1631       return HTTP_INTERNAL_SERVER_ERROR;
1632       }
1633
1634       virtualDirCallback.close(Fp);
1635       }
1636     */
1637    return HTTP_OK;
1638}
1639
1640/************************************************************************
1641* Function: web_server_callback
1642*
1643* Parameters:
1644*	IN http_parser_t *parser ; HTTP Parser Object
1645*	INOUT http_message_t* req ; HTTP Message request
1646*	IN SOCKINFO *info ;			Socket information object
1647*
1648* Description: main entry point into web server;
1649*	handles HTTP GET and HEAD requests
1650*
1651* Returns:
1652*	void
1653************************************************************************/
1654void
1655web_server_callback( IN http_parser_t * parser,
1656                     INOUT http_message_t * req,
1657                     IN SOCKINFO * info )
1658{
1659    int ret;
1660    int timeout = 0;
1661    enum resp_type rtype;
1662    membuffer headers;
1663    membuffer filename;
1664    struct xml_alias_t xmldoc;
1665    struct SendInstruction RespInstr;
1666
1667    //Initialize instruction header.
1668    RespInstr.IsVirtualFile = 0;
1669    RespInstr.IsChunkActive = 0;
1670    RespInstr.IsRangeActive = 0;
1671    RespInstr.IsTrailers = 0;
1672    // init
1673    membuffer_init( &headers );
1674    membuffer_init( &filename );
1675
1676    //Process request should create the different kind of header depending on the
1677    //the type of request.
1678    ret =
1679        process_request( req, &rtype, &headers, &filename, &xmldoc,
1680                         &RespInstr );
1681    if( ret != UPNP_E_SUCCESS ) {
1682        // send error code
1683        http_SendStatusResponse( info, ret, req->major_version,
1684                                 req->minor_version );
1685    } else {
1686        //
1687        // send response
1688        switch ( rtype ) {
1689            case RESP_FILEDOC: // send file, I = further instruction to send data.
1690                http_SendMessage( info, &timeout, "Ibf", &RespInstr,
1691                                  headers.buf, headers.length,
1692                                  filename.buf );
1693                break;
1694
1695            case RESP_XMLDOC:  // send xmldoc , I = further instruction to send data.
1696                http_SendMessage( info, &timeout, "Ibb", &RespInstr,
1697                                  headers.buf, headers.length,
1698                                  xmldoc.doc.buf, xmldoc.doc.length );
1699                alias_release( &xmldoc );
1700                break;
1701
1702            case RESP_WEBDOC:  //, I = further instruction to send data.
1703                /*
1704                   http_SendVirtualDirDoc( info, &timeout, "Ibf",&RespInstr,
1705                   headers.buf, headers.length,
1706                   filename.buf );
1707                 */
1708                http_SendMessage( info, &timeout, "Ibf", &RespInstr,
1709                                  headers.buf, headers.length,
1710                                  filename.buf );
1711                break;
1712
1713            case RESP_HEADERS: // headers only
1714                http_SendMessage( info, &timeout, "b",
1715                                  headers.buf, headers.length );
1716
1717                break;
1718            case RESP_POST:    // headers only
1719                ret =
1720                    http_RecvPostMessage( parser, info, filename.buf,
1721                                          &RespInstr );
1722                //Send response.
1723                http_MakeMessage( &headers, 1, 1, "RTDSCc", ret,
1724                                  "text/html" );
1725                http_SendMessage( info, &timeout, "b", headers.buf,
1726                                  headers.length );
1727                break;
1728
1729            default:
1730                assert( 0 );
1731        }
1732    }
1733
1734    DBGONLY( UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
1735                         "webserver: request processed...\n" );
1736         )
1737
1738        membuffer_destroy( &headers );
1739    membuffer_destroy( &filename );
1740}
1741