/**************************************************************************
 *
 * Copyright (c) 2000-2003 Intel Corporation 
 * All rights reserved. 
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 *
 * - Redistributions of source code must retain the above copyright notice, 
 * this list of conditions and the following disclaimer. 
 * - Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the documentation 
 * and/or other materials provided with the distribution. 
 * - Neither name of Intel Corporation nor the names of its contributors 
 * may be used to endorse or promote products derived from this software 
 * without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 **************************************************************************/


/******************************************************************************
 * Purpose: This file defines the Web Server and has functions to carry out
 * operations of the Web Server.
 ******************************************************************************/


#include "config.h"


#include "webserver.h"


#include "FileInfo.h"
#include "httpparser.h"
#include "httpreadwrite.h"
#include "ithread.h"
#include "membuffer.h"
#include "ssdplib.h"
#include "statcodes.h"
#include "strintmap.h"
#include "unixutil.h"
#include "upnp.h"
#include "upnpapi.h"
#include "util.h"
#include "VirtualDir.h"


#include <assert.h>
#include <fcntl.h>
#include <sys/stat.h>


#ifdef UPNP_USE_BCBPP
    /* Do not #include <inttypes.h> */
    /* Do not #include <stdint.h> */
#else
    #include <inttypes.h>
    #include <stdint.h>
#endif /* !UPNP_USE_BCBPP */


#ifdef WIN32
    /* Do not #include <unistd.h> */
#else
    #include <unistd.h>
#endif


/*
   Response Types 
 */
enum resp_type {
	RESP_FILEDOC,
	RESP_XMLDOC,
	RESP_HEADERS,
	RESP_WEBDOC,
	RESP_POST };

// mapping of file extension to content-type of document
struct document_type_t {
    const char *file_ext;
    const char *content_type;
    const char *content_subtype;
};

struct xml_alias_t {
    membuffer name;             // name of DOC from root; e.g.: /foo/bar/mydesc.xml
    membuffer doc;              // the XML document contents
    time_t last_modified;
    int *ct;
};

static const char *gMediaTypes[] = {
    NULL,                       // 0
    "audio",                    // 1
    "video",                    // 2
    "image",                    // 3
    "application",              // 4
    "text"                      // 5
};

/*
   Defines 
 */

// index into 'gMediaTypes'
#define AUDIO_STR        "\1"
#define VIDEO_STR        "\2"
#define IMAGE_STR        "\3"
#define APPLICATION_STR  "\4"
#define TEXT_STR         "\5"

// int index
#define APPLICATION_INDEX 4
#define TEXT_INDEX        5

// general
#define NUM_MEDIA_TYPES       69
#define NUM_HTTP_HEADER_NAMES 33

// sorted by file extension; must have 'NUM_MEDIA_TYPES' extensions
static const char *gEncodedMediaTypes =
    "aif\0" AUDIO_STR "aiff\0"
    "aifc\0" AUDIO_STR "aiff\0"
    "aiff\0" AUDIO_STR "aiff\0"
    "asf\0" VIDEO_STR "x-ms-asf\0"
    "asx\0" VIDEO_STR "x-ms-asf\0"
    "au\0" AUDIO_STR "basic\0"
    "avi\0" VIDEO_STR "msvideo\0"
    "bmp\0" IMAGE_STR "bmp\0"
    "dcr\0" APPLICATION_STR "x-director\0"
    "dib\0" IMAGE_STR "bmp\0"
    "dir\0" APPLICATION_STR "x-director\0"
    "dxr\0" APPLICATION_STR "x-director\0"
    "gif\0" IMAGE_STR "gif\0"
    "hta\0" TEXT_STR "hta\0"
    "htm\0" TEXT_STR "html\0"
    "html\0" TEXT_STR "html\0"
    "jar\0" APPLICATION_STR "java-archive\0"
    "jfif\0" IMAGE_STR "pjpeg\0"
    "jpe\0" IMAGE_STR "jpeg\0"
    "jpeg\0" IMAGE_STR "jpeg\0"
    "jpg\0" IMAGE_STR "jpeg\0"
    "js\0" APPLICATION_STR "x-javascript\0"
    "kar\0" AUDIO_STR "midi\0"
    "m3u\0" AUDIO_STR "mpegurl\0"
    "mid\0" AUDIO_STR "midi\0"
    "midi\0" AUDIO_STR "midi\0"
    "mov\0" VIDEO_STR "quicktime\0"
    "mp2v\0" VIDEO_STR "x-mpeg2\0"
    "mp3\0" AUDIO_STR "mpeg\0"
    "mpe\0" VIDEO_STR "mpeg\0"
    "mpeg\0" VIDEO_STR "mpeg\0"
    "mpg\0" VIDEO_STR "mpeg\0"
    "mpv\0" VIDEO_STR "mpeg\0"
    "mpv2\0" VIDEO_STR "x-mpeg2\0"
    "pdf\0" APPLICATION_STR "pdf\0"
    "pjp\0" IMAGE_STR "jpeg\0"
    "pjpeg\0" IMAGE_STR "jpeg\0"
    "plg\0" TEXT_STR "html\0"
    "pls\0" AUDIO_STR "scpls\0"
    "png\0" IMAGE_STR "png\0"
    "qt\0" VIDEO_STR "quicktime\0"
    "ram\0" AUDIO_STR "x-pn-realaudio\0"
    "rmi\0" AUDIO_STR "mid\0"
    "rmm\0" AUDIO_STR "x-pn-realaudio\0"
    "rtf\0" APPLICATION_STR "rtf\0"
    "shtml\0" TEXT_STR "html\0"
    "smf\0" AUDIO_STR "midi\0"
    "snd\0" AUDIO_STR "basic\0"
    "spl\0" APPLICATION_STR "futuresplash\0"
    "ssm\0" APPLICATION_STR "streamingmedia\0"
    "swf\0" APPLICATION_STR "x-shockwave-flash\0"
    "tar\0" APPLICATION_STR "tar\0"
    "tcl\0" APPLICATION_STR "x-tcl\0"
    "text\0" TEXT_STR "plain\0"
    "tif\0" IMAGE_STR "tiff\0"
    "tiff\0" IMAGE_STR "tiff\0"
    "txt\0" TEXT_STR "plain\0"
    "ulw\0" AUDIO_STR "basic\0"
    "wav\0" AUDIO_STR "wav\0"
    "wax\0" AUDIO_STR "x-ms-wax\0"
    "wm\0" VIDEO_STR "x-ms-wm\0"
    "wma\0" AUDIO_STR "x-ms-wma\0"
    "wmv\0" VIDEO_STR "x-ms-wmv\0"
    "wvx\0" VIDEO_STR "x-ms-wvx\0"
    "xbm\0" IMAGE_STR "x-xbitmap\0"
    "xml\0" TEXT_STR "xml\0"
    "xsl\0" TEXT_STR "xml\0"
    "z\0" APPLICATION_STR "x-compress\0"
    "zip\0" APPLICATION_STR "zip\0" "\0";
    // *** end ***

/************************************************************************
   module variables - Globals, static and externs                      
************************************************************************/
static struct document_type_t gMediaTypeList[NUM_MEDIA_TYPES];
membuffer gDocumentRootDir;     // a local dir which serves as webserver root
static struct xml_alias_t gAliasDoc;    // XML document
static ithread_mutex_t gWebMutex;
extern str_int_entry Http_Header_Names[NUM_HTTP_HEADER_NAMES];

/************************************************************************
 * Function: has_xml_content_type
 *
 * Parameters:
 *	none
 *
 * Description: decodes list and stores it in gMediaTypeList
 *
 * Returns:
 *	 void
 ************************************************************************/
static UPNP_INLINE void
media_list_init( void )
{
    int i;
    const char *s = gEncodedMediaTypes;
    struct document_type_t *doc_type;

    for( i = 0; *s != '\0'; i++ ) {
        doc_type = &gMediaTypeList[i];

        doc_type->file_ext = s;

        s += strlen( s ) + 1;   // point to type
        doc_type->content_type = gMediaTypes[( int )*s];    // set cont-type

        s++;                    // point to subtype
        doc_type->content_subtype = s;

        s += strlen( s ) + 1;   // next entry
    }
    assert( i == NUM_MEDIA_TYPES );
}

/************************************************************************
 * Function: has_xml_content_type
 *
 * Parameters:
 *	IN const char* extension ;
 *	OUT const char** con_type,
 *	OUT const char** con_subtype
 *
 * Description: Based on the extension, returns the content type and
 *	content subtype
 *
 * Returns:
 *	 0 on success;
 *	-1 on error
 ************************************************************************/
static UPNP_INLINE int
search_extension( IN const char *extension,
                  OUT const char **con_type,
                  OUT const char **con_subtype )
{
    int top,
      mid,
      bot;
    int cmp;

    top = 0;
    bot = NUM_MEDIA_TYPES - 1;

    while( top <= bot ) {
        mid = ( top + bot ) / 2;
        cmp = strcasecmp( extension, gMediaTypeList[mid].file_ext );

        if( cmp > 0 ) {
            top = mid + 1;      // look below mid
        } else if( cmp < 0 ) {
            bot = mid - 1;      // look above mid
        } else                  // cmp == 0
        {
            *con_type = gMediaTypeList[mid].content_type;
            *con_subtype = gMediaTypeList[mid].content_subtype;
            return 0;
        }
    }

    return -1;
}

/************************************************************************
 * Function: get_content_type
 *
 * Parameters:
 *	IN const char* filename,
 *	OUT UpnpFileInfo *fileInfo
 *
 * Description: Based on the extension, sets the content type of fileInfo
 *	based on type and content subtype. If content type and sub type
 *	are not found, unknown types are used
 *
 * Returns:
 *	 0 - On Sucess
 *	 UPNP_E_OUTOF_MEMORY - on memory allocation failures
 ************************************************************************/
UPNP_INLINE int
get_content_type(
	IN const char *filename,
	OUT UpnpFileInfo *fileInfo)
{
    const char *extension;
    const char *type;
    const char *subtype;
    xboolean ctype_found = FALSE;
    char *temp = NULL;
    int length = 0;

    UpnpFileInfo_set_ContentType(fileInfo, NULL);

    // get ext
    extension = strrchr( filename, '.' );
    if (extension != NULL) {
        if( search_extension( extension + 1, &type, &subtype ) == 0 ) {
            ctype_found = TRUE;
        }
    }

    if (!ctype_found) {
        // unknown content type
        type = gMediaTypes[APPLICATION_INDEX];
        subtype = "octet-stream";
    }

    length = strlen( type ) + strlen( "/" ) + strlen( subtype ) + 1;
    temp = ( char * )malloc( length );
    if (!temp) {
        return UPNP_E_OUTOF_MEMORY;
    }
    sprintf( temp, "%s/%s", type, subtype );
    UpnpFileInfo_set_ContentType(fileInfo, temp);
    free(temp);

    if (!UpnpFileInfo_get_ContentType(fileInfo)) {
        return UPNP_E_OUTOF_MEMORY;
    }

    return 0;
}

/************************************************************************
 * Function: glob_alias_init
 *
 * Parameters:
 *	none
 *
 * Description: Initialize the global XML document. Allocate buffers
 *	for the XML document
 *
 * Returns:
 *	 void
 ************************************************************************/
static UPNP_INLINE void
glob_alias_init( void )
{
    struct xml_alias_t *alias = &gAliasDoc;

    membuffer_init( &alias->doc );
    membuffer_init( &alias->name );
    alias->ct = NULL;
    alias->last_modified = 0;
}

/************************************************************************
 * Function: is_valid_alias
 *
 * Parameters:
 *	IN const struct xml_alias_t* alias ; XML alias object
 *
 * Description: Check for the validity of the XML object buffer
 *
 * Returns:
 *	 BOOLEAN
 ************************************************************************/
static UPNP_INLINE xboolean
is_valid_alias( IN const struct xml_alias_t *alias )
{
    return alias->doc.buf != NULL;
}

/************************************************************************
 * Function: alias_grab
 *
 * Parameters:
 *	OUT struct xml_alias_t* alias ; XML alias object
 *
 * Description: Copy the contents of the global XML document into the
 *	local OUT parameter
 *
 * Returns:
 *	 void
 ************************************************************************/
static void
alias_grab( OUT struct xml_alias_t *alias )
{
    ithread_mutex_lock( &gWebMutex );

    assert( is_valid_alias( &gAliasDoc ) );

    memcpy( alias, &gAliasDoc, sizeof( struct xml_alias_t ) );
    *alias->ct = *alias->ct + 1;

    ithread_mutex_unlock( &gWebMutex );
}

/************************************************************************
 * Function: alias_release
 *
 * Parameters:
 *	IN struct xml_alias_t* alias ; XML alias object
 *
 * Description: Release the XML document referred to by the IN parameter
 *	Free the allocated buffers associated with this object
 *
 * Returns:
 *	void
 ************************************************************************/
static void
alias_release( IN struct xml_alias_t *alias )
{
    ithread_mutex_lock( &gWebMutex );

    // ignore invalid alias
    if( !is_valid_alias( alias ) ) {
        ithread_mutex_unlock( &gWebMutex );
        return;
    }

    assert( alias->ct > 0 );

    *alias->ct = *alias->ct - 1;
    if( *alias->ct <= 0 ) {
        membuffer_destroy( &alias->doc );
        membuffer_destroy( &alias->name );
        free( alias->ct );
    }
    ithread_mutex_unlock( &gWebMutex );
}

/************************************************************************
 * Function: web_server_set_alias
 *
 * Parameters:
 *	alias_name: webserver name of alias; created by caller and freed by
 *		caller (doesn't even have to be malloc()d .)
 *	alias_content:	the xml doc; this is allocated by the caller; and
 *		freed by the web server
 *	alias_content_length: length of alias body in bytes
 *	last_modified:	time when the contents of alias were last
 *		changed (local time)
 *
 * Description: Replaces current alias with the given alias. To remove
 *	the current alias, set alias_name to NULL.
 *
 * Returns:
 *	0 - OK
 *	UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here
 ************************************************************************/
int
web_server_set_alias( IN const char *alias_name,
                      IN const char *alias_content,
                      IN size_t alias_content_length,
                      IN time_t last_modified )
{
    int ret_code;
    struct xml_alias_t alias;

    alias_release( &gAliasDoc );

    if( alias_name == NULL ) {
        // don't serve aliased doc anymore
        return 0;
    }

    assert( alias_content != NULL );

    membuffer_init( &alias.doc );
    membuffer_init( &alias.name );
    alias.ct = NULL;

    do {
        // insert leading /, if missing
        if( *alias_name != '/' ) {
            if( membuffer_assign_str( &alias.name, "/" ) != 0 ) {
                break;          // error; out of mem
            }
        }

        ret_code = membuffer_append_str( &alias.name, alias_name );
        if( ret_code != 0 ) {
            break;              // error
        }

        if( ( alias.ct = ( int * )malloc( sizeof( int ) ) ) == NULL ) {
            break;              // error
        }
        *alias.ct = 1;
        membuffer_attach( &alias.doc, ( char * )alias_content,
                          alias_content_length );

        alias.last_modified = last_modified;

        // save in module var
        ithread_mutex_lock( &gWebMutex );
        gAliasDoc = alias;
        ithread_mutex_unlock( &gWebMutex );

        return 0;
    } while( FALSE );

    // error handler

    // free temp alias
    membuffer_destroy( &alias.name );
    membuffer_destroy( &alias.doc );
    free( alias.ct );
    return UPNP_E_OUTOF_MEMORY;
}


int web_server_init()
{
	int ret = 0;
	if (bWebServerState == WEB_SERVER_DISABLED) {
		// decode media list
		media_list_init();
		membuffer_init(&gDocumentRootDir);
		glob_alias_init();
		pVirtualDirList = NULL;

		// Initialize callbacks
		virtualDirCallback.get_info = NULL;
		virtualDirCallback.open = NULL;
		virtualDirCallback.read = NULL;
		virtualDirCallback.write = NULL;
		virtualDirCallback.seek = NULL;
		virtualDirCallback.close = NULL;

		if (ithread_mutex_init(&gWebMutex, NULL) == -1) {
			ret = UPNP_E_OUTOF_MEMORY;
		} else {
			bWebServerState = WEB_SERVER_ENABLED;
		}
	}

	return ret;
}

/************************************************************************
 * Function: web_server_destroy
 *
 * Parameters:
 *	none
 *
 * Description: Release memory allocated for the global web server root
 *	directory and the global XML document
 *	Resets the flag bWebServerState to WEB_SERVER_DISABLED
 *
 * Returns:
 *	void
 ************************************************************************/
void
web_server_destroy( void )
{
    int ret;

    if( bWebServerState == WEB_SERVER_ENABLED ) {
        membuffer_destroy( &gDocumentRootDir );
        alias_release( &gAliasDoc );

        ithread_mutex_lock( &gWebMutex );
        memset( &gAliasDoc, 0, sizeof( struct xml_alias_t ) );
        ithread_mutex_unlock( &gWebMutex );

        ret = ithread_mutex_destroy( &gWebMutex );
        assert( ret == 0 );
        bWebServerState = WEB_SERVER_DISABLED;
    }
}

/************************************************************************
 * Function: get_file_info
 *
 * Parameters:
 *	IN const char *filename;   Filename having the description document
 *	OUT UpnpFileInfo *info;        File information object having file 
 *		attributes such as filelength, when was the file last
 *		modified, whether a file or a directory and whether the
 *		file or directory is readable.
 *
 * Description: Release memory allocated for the global web server root
 *	directory and the global XML document
 *	Resets the flag bWebServerState to WEB_SERVER_DISABLED
 *
 * Returns:
 *	int
 ************************************************************************/
static int
get_file_info(
	IN const char *filename,
	OUT UpnpFileInfo *info)
{
	int code;
	struct stat s;
	FILE *fp;
	int rc = 0;

	UpnpFileInfo_set_ContentType(info, NULL);

	code = stat(filename, &s);
	if (code == -1) {
		return -1;
	}

	if (S_ISDIR(s.st_mode)) {
		UpnpFileInfo_set_IsDirectory(info, TRUE);
	} else if (S_ISREG(s.st_mode)) {
		UpnpFileInfo_set_IsDirectory(info, FALSE);
	} else {
		return -1;
	}

	// check readable
	fp = fopen(filename, "r");
	UpnpFileInfo_set_IsReadable(info, fp != NULL);
	if (fp) {
		fclose(fp);
	}

	UpnpFileInfo_set_FileLength(info, s.st_size);
	UpnpFileInfo_set_LastModified(info, &s.st_mtime);

	rc = get_content_type(filename, info);

	UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
		"file info: %s, length: %lld, last_mod=%s readable=%d\n",
		filename,
		(long long)UpnpFileInfo_get_FileLength(info),
		asctime(gmtime(UpnpFileInfo_get_LastModified(info))),
		UpnpFileInfo_get_IsReadable(info));

	return rc;
}

/************************************************************************
 * Function: web_server_set_root_dir
 *
 * Parameters:
 *	IN const char* root_dir ; String having the root directory for the
 *		document
 *
 * Description: Assign the path specfied by the IN const char* root_dir
 *	parameter to the global Document root directory. Also check for
 *	path names ending in '/'
 *
 * Returns:
 *	int
 ************************************************************************/
int
web_server_set_root_dir( IN const char *root_dir )
{
    int index;
    int ret;

    ret = membuffer_assign_str( &gDocumentRootDir, root_dir );
    if( ret != 0 ) {
        return ret;
    }
    // remove trailing '/', if any
    if( gDocumentRootDir.length > 0 ) {
        index = gDocumentRootDir.length - 1;    // last char
        if( gDocumentRootDir.buf[index] == '/' ) {
            membuffer_delete( &gDocumentRootDir, index, 1 );
        }
    }

    return 0;
}

/************************************************************************
 * Function: get_alias
 *
 * Parameters:
 *	IN const char *request_file;    request file passed in to be compared with
 *	OUT struct xml_alias_t* alias ; xml alias object which has a file name
 *		stored
 *      OUT UpnpFileInfo *info;             File information object which will be
 *		filled up if the file comparison succeeds
 *
 * Description: Compare the files names between the one on the XML alias
 *	the one passed in as the input parameter. If equal extract file
 *	information
 *
 * Returns:
 *	TRUE - On Success
 *	FALSE if request is not an alias
 ************************************************************************/
static UPNP_INLINE xboolean
get_alias(
	IN const char *request_file,
	OUT struct xml_alias_t *alias,
	OUT UpnpFileInfo *info)
{
	int cmp = strcmp(alias->name.buf, request_file);
	if (cmp == 0) {
		// fill up info
		UpnpFileInfo_set_FileLength(info, alias->doc.length);
		UpnpFileInfo_set_IsDirectory(info, FALSE);
		UpnpFileInfo_set_IsReadable(info, TRUE);
		UpnpFileInfo_set_LastModified(info, &alias->last_modified);
	}

	return cmp == 0;
}

/************************************************************************
 * Function: isFileInVirtualDir
 *
 * Parameters:
 *	IN char *filePath ; directory path to be tested for virtual directory
 *
 * Description: Compares filePath with paths from the list of virtual
 *	directory lists
 *
 * Returns:
 *	BOOLEAN	
 ************************************************************************/
int
isFileInVirtualDir( IN char *filePath )
{
    virtualDirList *pCurVirtualDir;
    int webDirLen;

    pCurVirtualDir = pVirtualDirList;
    while( pCurVirtualDir != NULL ) {
        webDirLen = strlen( pCurVirtualDir->dirName );
        if( pCurVirtualDir->dirName[webDirLen - 1] == '/' ) {
            if( strncmp( pCurVirtualDir->dirName, filePath, webDirLen ) ==
                0 )
                return TRUE;
        } else {
            if( ( strncmp( pCurVirtualDir->dirName, filePath, webDirLen )
                  == 0 ) && ( filePath[webDirLen] == '/' || filePath[webDirLen] == '\0' || filePath[webDirLen] == '?' ) )
                return TRUE;
        }

        pCurVirtualDir = pCurVirtualDir->next;
    }

    return FALSE;
}

/************************************************************************
 * Function: ToUpperCase
 *
 * Parameters:
 *	INOUT char * Str ; Input string to be converted
 *
 * Description: Converts input string to upper case
 *
 * Returns:
 *	int
 ************************************************************************/
int
ToUpperCase( char *Str )
{
    int i;

    for( i = 0; i < ( int )strlen( Str ); i++ )
        Str[i] = toupper( Str[i] );
    return 1;

}

/************************************************************************
 * Function: StrStr
 *
 * Parameters:
 *	IN char * S1 ; Input string
 *	IN char * S2 ; Input sub-string
 *
 * Description: Finds a substring from a string
 *
 * Returns:
 *	char * ptr - pointer to the first occurence of S2 in S1
 ************************************************************************/
char *
StrStr( char *S1,
        char *S2 )
{
    char *Str1,
     *Str2;
    char *Ptr,
     *Ret;
    int Pos;

    Str1 = ( char * )malloc( strlen( S1 ) + 2 );
    if(!Str1) return NULL;
    Str2 = ( char * )malloc( strlen( S2 ) + 2 );
    if (!Str2)
       {
       free(Str1);
       return NULL;
       }

    strcpy( Str1, S1 );
    strcpy( Str2, S2 );

    ToUpperCase( Str1 );
    ToUpperCase( Str2 );
    Ptr = strstr( Str1, Str2 );
    if( Ptr == NULL )
        return NULL;

    Pos = Ptr - Str1;

    Ret = S1 + Pos;

    free( Str1 );
    free( Str2 );
    return Ret;

}

/************************************************************************
 * Function: StrTok
 *
 * Parameters:
 *	IN char ** Src ; String containing the token
 *	IN char * del ; Set of delimiter characters
 *
 * Description: Finds next token in a string
 *
 * Returns:
 *	char * ptr - pointer to the first occurence of S2 in S1
 ************************************************************************/
char *
StrTok( char **Src,
        char *Del )
{
    char *TmpPtr,
     *RetPtr;

    if( *Src != NULL ) {
        RetPtr = *Src;
        TmpPtr = strstr( *Src, Del );
        if( TmpPtr != NULL ) {
            *TmpPtr = '\0';
            *Src = TmpPtr + strlen( Del );
        } else
            *Src = NULL;

        return RetPtr;
    }

    return NULL;
}

/************************************************************************
 * Function: GetNextRange
 *
 * Parameters:
 *	IN char ** SrcRangeStr ; string containing the token / range
 *	OUT int * FirstByte ;	 gets the first byte of the token
 *	OUT int * LastByte	; gets the last byte of the token
 *
 * Description: Returns a range of integers from a string
 *
 * Returns: int	;
 *	always returns 1;
 ************************************************************************/
int
GetNextRange( char **SrcRangeStr,
              off_t *FirstByte,
              off_t *LastByte )
{
    char *Ptr;
    char *Tok;
    int i;
    int64_t F = -1;
    int64_t L = -1;
    int Is_Suffix_byte_Range = 1;

    if( *SrcRangeStr == NULL ) {
        return -1;
    }

    Tok = StrTok( SrcRangeStr, "," );

    if( ( Ptr = strstr( Tok, "-" ) ) == NULL ) {
        return -1;
    }
    *Ptr = ' ';
    sscanf( Tok, "%"SCNd64"%"SCNd64, &F, &L );

    if( F == -1 || L == -1 ) {
        *Ptr = '-';
        for( i = 0; i < ( int )strlen( Tok ); i++ ) {
            if( Tok[i] == '-' ) {
                break;
            } else if( isdigit( Tok[i] ) ) {
                Is_Suffix_byte_Range = 0;
                break;
            }

        }

        if( Is_Suffix_byte_Range ) {
            *FirstByte = (off_t)L;
            *LastByte = (off_t)F;
            return 1;
        }
    }
    *FirstByte = (off_t)F;
    *LastByte = (off_t)L;

    return 1;
}

/************************************************************************
 * Function: CreateHTTPRangeResponseHeader
 *
 * Parameters:
 *	char * ByteRangeSpecifier ; String containing the range
 *	long FileLength ; Length of the file
 *	OUT struct SendInstruction * Instr ; SendInstruction object
 *		where the range operations will be stored
 *
 * Description: Fills in the Offset, read size and contents to send out
 *	as an HTTP Range Response
 *
 * Returns:
 *	HTTP_BAD_REQUEST
 *	UPNP_E_OUTOF_MEMORY
 *	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
 *	HTTP_OK	
 ************************************************************************/
int
CreateHTTPRangeResponseHeader( char *ByteRangeSpecifier,
                               off_t FileLength,
                               OUT struct SendInstruction *Instr )
{

    off_t FirstByte,
      LastByte;
    char *RangeInput,
     *Ptr;

    Instr->IsRangeActive = 1;
    Instr->ReadSendSize = FileLength;

    if( !ByteRangeSpecifier )
        return HTTP_BAD_REQUEST;

    RangeInput = malloc( strlen( ByteRangeSpecifier ) + 1 );
    if( !RangeInput )
        return UPNP_E_OUTOF_MEMORY;
    strcpy( RangeInput, ByteRangeSpecifier );

    //CONTENT-RANGE: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
    if( StrStr( RangeInput, "bytes" ) == NULL ||
        ( Ptr = StrStr( RangeInput, "=" ) ) == NULL ) {
        free( RangeInput );
        Instr->IsRangeActive = 0;
        return HTTP_BAD_REQUEST;
    }
    //Jump =
    Ptr = Ptr + 1;

    if( FileLength < 0 ) {
        free( RangeInput );
        return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
    }

    if( GetNextRange( &Ptr, &FirstByte, &LastByte ) != -1 ) {

        if( FileLength < FirstByte ) {
            free( RangeInput );
            return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
        }

        if( FirstByte >= 0 && LastByte >= 0 && LastByte >= FirstByte ) {
            if( LastByte >= FileLength )
                LastByte = FileLength - 1;

            Instr->RangeOffset = FirstByte;
            Instr->ReadSendSize = LastByte - FirstByte + 1;
            sprintf( Instr->RangeHeader,
                "CONTENT-RANGE: bytes %"PRId64"-%"PRId64"/%"PRId64"\r\n",
                (int64_t)FirstByte,
                (int64_t)LastByte,
                (int64_t)FileLength );   //Data between two range.
        } else if( FirstByte >= 0 && LastByte == -1
                   && FirstByte < FileLength ) {
            Instr->RangeOffset = FirstByte;
            Instr->ReadSendSize = FileLength - FirstByte;
            sprintf( Instr->RangeHeader,
                     "CONTENT-RANGE: bytes %"PRId64"-%"PRId64"/%"PRId64"\r\n",
                     (int64_t)FirstByte,
                     (int64_t)(FileLength - 1),
                     (int64_t)FileLength );
        } else if( FirstByte == -1 && LastByte > 0 ) {
            if( LastByte >= FileLength ) {
                Instr->RangeOffset = 0;
                Instr->ReadSendSize = FileLength;
                sprintf( Instr->RangeHeader,
                         "CONTENT-RANGE: bytes 0-%"PRId64"/%"PRId64"\r\n",
                         (int64_t)(FileLength - 1),
                         (int64_t)FileLength );
            } else {
                Instr->RangeOffset = FileLength - LastByte;
                Instr->ReadSendSize = LastByte;
                sprintf( Instr->RangeHeader,
                         "CONTENT-RANGE: bytes %"PRId64"-%"PRId64"/%"PRId64"\r\n",
                         (int64_t)(FileLength - LastByte + 1),
                         (int64_t)FileLength,
                         (int64_t)FileLength );
            }
        } else {
            free( RangeInput );
            return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
        }
    } else {
        free( RangeInput );
        return HTTP_REQUEST_RANGE_NOT_SATISFIABLE;
    }

    free( RangeInput );
    return HTTP_OK;
}

/************************************************************************
 * Function: CheckOtherHTTPHeaders
 *
 * Parameters:
 *	IN http_message_t * Req ;  HTTP Request message
 *	OUT struct SendInstruction * RespInstr ; Send Instruction object to
 *		data for the response
 *	int FileSize ;	Size of the file containing the request document
 *
 * Description: Get header id from the request parameter and take
 *	appropriate action based on the ids.
 *	as an HTTP Range Response
 *
 * Returns:
 *	HTTP_BAD_REQUEST
 *	UPNP_E_OUTOF_MEMORY
 *	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
 *	HTTP_OK
 ************************************************************************/
int
CheckOtherHTTPHeaders( IN http_message_t * Req,
                       OUT struct SendInstruction *RespInstr,
                       off_t FileSize )
{
    http_header_t *header;
    ListNode *node;

    //NNS: dlist_node* node;
    int index,
      RetCode = HTTP_OK;
    char *TmpBuf;

    TmpBuf = ( char * )malloc( LINE_SIZE );
    if( !TmpBuf )
        return UPNP_E_OUTOF_MEMORY;

    node = ListHead( &Req->headers );

    while( node != NULL ) {
        header = ( http_header_t * ) node->item;

        // find header type.
        index = map_str_to_int( ( const char * )header->name.buf,
                                header->name.length, Http_Header_Names,
                                NUM_HTTP_HEADER_NAMES, FALSE );

        if( header->value.length >= LINE_SIZE ) {
            free( TmpBuf );
            TmpBuf = ( char * )malloc( header->value.length + 1 );
            if( !TmpBuf )
                return UPNP_E_OUTOF_MEMORY;
        }

        memcpy( TmpBuf, header->value.buf, header->value.length );
        TmpBuf[header->value.length] = '\0';
        if( index >= 0 )
            switch ( Http_Header_Names[index].id ) {
                case HDR_TE:   //Request
                    {
                        RespInstr->IsChunkActive = 1;

                        if( strlen( TmpBuf ) > strlen( "gzip" ) ) {
                            if( StrStr( TmpBuf, "trailers" ) != NULL ) {    //means client will accept trailer
                                RespInstr->IsTrailers = 1;
                            }
                        }
                    }
                    break;

                case HDR_CONTENT_LENGTH:
                    {
                        RespInstr->RecvWriteSize = atoi( TmpBuf );
                        break;
                    }

                case HDR_RANGE:
                    if( ( RetCode = CreateHTTPRangeResponseHeader( TmpBuf,
                                                                   FileSize,
                                                                   RespInstr ) )
                        != HTTP_OK ) {
                        free( TmpBuf );
                        return RetCode;
                    }
                    break;
                default:
                    /*
                       TODO 
                     */
                    /*
                       header.value is the value. 
                     */
                    /*
                       case HDR_CONTENT_TYPE: //return 1;
                       case HDR_CONTENT_LANGUAGE://return 1;
                       case HDR_LOCATION: //return 1;
                       case HDR_CONTENT_LOCATION://return 1;
                       case HDR_ACCEPT: //return 1;
                       case HDR_ACCEPT_CHARSET://return 1;
                       case HDR_ACCEPT_LANGUAGE://return 1;
                       case HDR_USER_AGENT: break;//return 1;
                     */

                    //Header check for encoding 
                    /*
                       case HDR_ACCEPT_RANGE: //Server capability.
                       case HDR_CONTENT_RANGE://Response.
                       case HDR_IF_RANGE:
                     */

                    //Header check for encoding 
                    /*
                       case HDR_ACCEPT_ENCODING:
                       if(StrStr(TmpBuf, "identity"))
                       {
                       break;
                       }
                       else return -1;
                       case HDR_CONTENT_ENCODING:
                       case HDR_TRANSFER_ENCODING: //Response
                     */
                    break;
            }

        node = ListNext( &Req->headers, node );

    }

    free( TmpBuf );
    return RetCode;
}

/************************************************************************
 * Function: process_request
 *
 * Parameters:
 *	IN http_message_t *req ; HTTP Request message
 *	OUT enum resp_type *rtype ; Tpye of response
 *	OUT membuffer *headers ;
 *	OUT membuffer *filename ; Get filename from request document
 *	OUT struct xml_alias_t *alias ; Xml alias document from the
 *		request document,
 *	OUT struct SendInstruction * RespInstr ; Send Instruction object
 *		where the response is set up.
 *
 * Description: Processes the request and returns the result in the OUT
 *	parameters
 *
 * Returns:
 *	HTTP_BAD_REQUEST
 *	UPNP_E_OUTOF_MEMORY
 *	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
 *	HTTP_OK
 ************************************************************************/
static int
process_request( IN http_message_t * req,
                 OUT enum resp_type *rtype,
                 OUT membuffer * headers,
                 OUT membuffer * filename,
                 OUT struct xml_alias_t *alias,
                 OUT struct SendInstruction *RespInstr )
{
    int code;
    int err_code;

    char *request_doc;
    UpnpFileInfo *finfo;
    xboolean using_alias;
    xboolean using_virtual_dir;
    uri_type *url;
    char *temp_str;
    int resp_major;
    int resp_minor;
    xboolean alias_grabbed;
    size_t dummy;
    const char *extra_headers;

    print_http_headers( req );

    url = &req->uri;
    assert( req->method == HTTPMETHOD_GET ||
            req->method == HTTPMETHOD_HEAD
            || req->method == HTTPMETHOD_POST
            || req->method == HTTPMETHOD_SIMPLEGET );

    // init
    request_doc = NULL;
    finfo = UpnpFileInfo_new();
    alias_grabbed = FALSE;
    err_code = HTTP_INTERNAL_SERVER_ERROR;  // default error
    using_virtual_dir = FALSE;
    using_alias = FALSE;

    http_CalcResponseVersion( req->major_version, req->minor_version,
                              &resp_major, &resp_minor );

    //
    // remove dots
    //
    request_doc = malloc( url->pathquery.size + 1 );
    if( request_doc == NULL ) {
        goto error_handler;     // out of mem
    }
    memcpy( request_doc, url->pathquery.buff, url->pathquery.size );
    request_doc[url->pathquery.size] = '\0';
    dummy = url->pathquery.size;
    remove_escaped_chars( request_doc, &dummy );
    code = remove_dots( request_doc, url->pathquery.size );
    if( code != 0 ) {
        err_code = HTTP_FORBIDDEN;
        goto error_handler;
    }

    if( *request_doc != '/' ) {
        // no slash
        err_code = HTTP_BAD_REQUEST;
        goto error_handler;
    }

    if( isFileInVirtualDir( request_doc ) ) {
        using_virtual_dir = TRUE;
        RespInstr->IsVirtualFile = 1;
        if( membuffer_assign_str( filename, request_doc ) != 0 ) {
            goto error_handler;
        }

    } else {
        //
        // try using alias
        //
        if (is_valid_alias(&gAliasDoc)) {
            alias_grab(alias);
            alias_grabbed = TRUE;
            using_alias = get_alias(request_doc, alias, finfo);
            if (using_alias == TRUE) {
                UpnpFileInfo_set_ContentType(finfo, "text/xml");
                if (UpnpFileInfo_get_ContentType(finfo) == NULL) {
                    goto error_handler;
                }
            }
        }
    }

    if (using_virtual_dir) {
        if (req->method != HTTPMETHOD_POST) {
            // get file info
            if (virtualDirCallback.get_info(filename->buf, finfo) != 0) {
                err_code = HTTP_NOT_FOUND;
                goto error_handler;
            }
            // try index.html if req is a dir
            if (UpnpFileInfo_get_IsDirectory(finfo)) {
                if (filename->buf[filename->length - 1] == '/') {
                    temp_str = "index.html";
                } else {
                    temp_str = "/index.html";
                }
                if ( membuffer_append_str(filename, temp_str) != 0) {
                    goto error_handler;
                }
                // get info
                if (virtualDirCallback.get_info(filename->buf, finfo) != UPNP_E_SUCCESS ||
                    UpnpFileInfo_get_IsDirectory(finfo)) {
                    err_code = HTTP_NOT_FOUND;
                    goto error_handler;
                }
            }
            // not readable
            if (!UpnpFileInfo_get_IsReadable(finfo)) {
                err_code = HTTP_FORBIDDEN;
                goto error_handler;
            }
            // finally, get content type
            // if ( get_content_type(filename->buf, &content_type) != 0 )
            //{
            //  goto error_handler;
            // }
        }
    } else if (!using_alias) {
        if( gDocumentRootDir.length == 0 ) {
            goto error_handler;
        }
        //
        // get file name
        //

        // filename str
        if( membuffer_assign_str( filename, gDocumentRootDir.buf ) != 0 ||
            membuffer_append_str( filename, request_doc ) != 0 ) {
            goto error_handler; // out of mem
        }
        // remove trailing slashes
        while( filename->length > 0 &&
               filename->buf[filename->length - 1] == '/' ) {
            membuffer_delete( filename, filename->length - 1, 1 );
        }

        if( req->method != HTTPMETHOD_POST ) {
            // get info on file
            if (get_file_info(filename->buf, finfo) != 0) {
                err_code = HTTP_NOT_FOUND;
                goto error_handler;
            }
            // try index.html if req is a dir
            if (UpnpFileInfo_get_IsDirectory(finfo)) {
                if( filename->buf[filename->length - 1] == '/' ) {
                    temp_str = "index.html";
                } else {
                    temp_str = "/index.html";
                }
                if( membuffer_append_str( filename, temp_str ) != 0 ) {
                    goto error_handler;
                }
                // get info
                if (get_file_info(filename->buf, finfo) != 0 ||
                    UpnpFileInfo_get_IsDirectory(finfo)) {
                    err_code = HTTP_NOT_FOUND;
                    goto error_handler;
                }
            }
            // not readable
            if (!UpnpFileInfo_get_IsReadable(finfo)) {
                err_code = HTTP_FORBIDDEN;
                goto error_handler;
            }

        }
        // finally, get content type
        //      if ( get_content_type(filename->buf, &content_type) != 0 )
        //      {
        //          goto error_handler;
        //      }
    }

    RespInstr->ReadSendSize = UpnpFileInfo_get_FileLength(finfo);

    // Check other header field.
    err_code =
        CheckOtherHTTPHeaders(req, RespInstr, UpnpFileInfo_get_FileLength(finfo));
    if ( err_code != HTTP_OK ) {
        goto error_handler;
    }

    if( req->method == HTTPMETHOD_POST ) {
        *rtype = RESP_POST;
        err_code = UPNP_E_SUCCESS;
        goto error_handler;
    }

    extra_headers = UpnpFileInfo_get_ExtraHeaders(finfo);
    if (!extra_headers) {
	    extra_headers = "";
    }

    if( RespInstr->IsRangeActive && RespInstr->IsChunkActive ) {
        // Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
        // Transfer-Encoding: chunked
        if (http_MakeMessage(
            headers, resp_major, resp_minor,
            "R" "T" "GKD" "s" "tcS" "Xc" "sCc",
            HTTP_PARTIAL_CONTENT,           // status code
            UpnpFileInfo_get_ContentType(finfo), // content type
            RespInstr,                      // range info
            "LAST-MODIFIED: ",
	    UpnpFileInfo_get_LastModified(finfo),
            X_USER_AGENT,
            extra_headers) != 0 ) {
            goto error_handler;
        }
    } else if( RespInstr->IsRangeActive && !RespInstr->IsChunkActive ) {
        // Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
        // Transfer-Encoding: chunked
        if (http_MakeMessage(
            headers, resp_major, resp_minor,
            "R" "N" "T" "GD" "s" "tcS" "Xc" "sCc",
            HTTP_PARTIAL_CONTENT,           // status code
            RespInstr->ReadSendSize,        // content length
            UpnpFileInfo_get_ContentType(finfo), // content type
            RespInstr,                      // range info
            "LAST-MODIFIED: ",
	    UpnpFileInfo_get_LastModified(finfo),
            X_USER_AGENT,
            extra_headers) != 0 ) {
            goto error_handler;
        }

    } else if( !RespInstr->IsRangeActive && RespInstr->IsChunkActive ) {
        // Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
        // Transfer-Encoding: chunked
        if (http_MakeMessage(
            headers, resp_major, resp_minor,
            "RK" "TD" "s" "tcS" "Xc" "sCc",
            HTTP_OK,                        // status code
            UpnpFileInfo_get_ContentType(finfo), // content type
            "LAST-MODIFIED: ",
	    UpnpFileInfo_get_LastModified(finfo),
            X_USER_AGENT,
            extra_headers) != 0 ) {
            goto error_handler;
        }

    } else { // !RespInstr->IsRangeActive && !RespInstr->IsChunkActive
        if (RespInstr->ReadSendSize >= 0) {
            // Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
            // Transfer-Encoding: chunked
            if (http_MakeMessage(
                headers, resp_major, resp_minor,
                "R" "N" "TD" "s" "tcS" "Xc" "sCc",
                HTTP_OK,                        // status code
                RespInstr->ReadSendSize,        // content length
                UpnpFileInfo_get_ContentType(finfo), // content type
                "LAST-MODIFIED: ",
	        UpnpFileInfo_get_LastModified(finfo),
                X_USER_AGENT,
                extra_headers) != 0 ) {
                goto error_handler;
            }
        } else {
            // Content-Range: bytes 222-3333/4000  HTTP_PARTIAL_CONTENT
            // Transfer-Encoding: chunked
            if (http_MakeMessage(
                headers, resp_major, resp_minor,
                "R" "TD" "s" "tcS" "b" "Xc" "sCc",
                HTTP_OK,                        // status code
                UpnpFileInfo_get_ContentType(finfo), // content type
                "LAST-MODIFIED: ",
	        UpnpFileInfo_get_LastModified(finfo),
                X_USER_AGENT,
                extra_headers) != 0 ) {
                goto error_handler;
            }
        }
    }

    if( req->method == HTTPMETHOD_HEAD ) {
        *rtype = RESP_HEADERS;
    } else if( using_alias ) {
        // GET xml
        *rtype = RESP_XMLDOC;
    } else if( using_virtual_dir ) {
        *rtype = RESP_WEBDOC;
    } else {
        // GET filename
        *rtype = RESP_FILEDOC;
    }

    // simple get http 0.9 as specified in http 1.0
    // don't send headers
    if( req->method == HTTPMETHOD_SIMPLEGET ) {
        membuffer_destroy( headers );
    }

    err_code = UPNP_E_SUCCESS;

error_handler:
    free(request_doc);
    UpnpFileInfo_delete(finfo);
    if (err_code != UPNP_E_SUCCESS && alias_grabbed) {
        alias_release(alias);
    }

    return err_code;
}

/************************************************************************
 * Function: http_RecvPostMessage
 *
 * Parameters:
 *	http_parser_t* parser ; HTTP Parser object
 *	IN SOCKINFO *info ; Socket Information object
 *	char * filename ; 	File where received data is copied to
 *	struct SendInstruction * Instr	; Send Instruction object which gives
 *		information whether the file is a virtual file or not.
 *
 * Description: Receives the HTTP post message
 *
 * Returns:
 *	HTTP_INTERNAL_SERVER_ERROR
 *	HTTP_UNAUTHORIZED
 *	HTTP_REQUEST_RANGE_NOT_SATISFIABLE
 *	HTTP_OK
 ************************************************************************/
int
http_RecvPostMessage( http_parser_t *parser,
                      IN SOCKINFO *info,
                      char *filename,
                      struct SendInstruction *Instr )
{

    unsigned int Data_Buf_Size = 1024;
    char Buf[1024];
    int Timeout = 0;
    long Num_Write = 0;
    FILE *Fp;
    parse_status_t status = PARSE_OK;
    xboolean ok_on_close = FALSE;
    unsigned int entity_offset = 0;
    int num_read = 0;
    int ret_code = 0;

    if( Instr && Instr->IsVirtualFile ) {
        Fp = (virtualDirCallback.open)( filename, UPNP_WRITE );
        if( Fp == NULL ) {
            return HTTP_INTERNAL_SERVER_ERROR;
        }
    } else {
        Fp = fopen( filename, "wb" );
        if( Fp == NULL ) {
            return HTTP_UNAUTHORIZED;
        }
    }

    parser->position = POS_ENTITY;

    do {
        //first parse what has already been gotten
        if( parser->position != POS_COMPLETE ) {
            status = parser_parse_entity( parser );
        }

        if( status == PARSE_INCOMPLETE_ENTITY ) {
            // read until close
            ok_on_close = TRUE;
        } else if( ( status != PARSE_SUCCESS )
                   && ( status != PARSE_CONTINUE_1 )
                   && ( status != PARSE_INCOMPLETE ) ) {
            //error
            fclose( Fp );
            return HTTP_BAD_REQUEST;
        }
        //read more if necessary entity
        while( ( ( entity_offset + Data_Buf_Size ) >
                 parser->msg.entity.length )
               && ( parser->position != POS_COMPLETE ) ) {
            num_read = sock_read( info, Buf, sizeof( Buf ), &Timeout );
            if( num_read > 0 ) {
                // append data to buffer
                ret_code = membuffer_append( &parser->msg.msg,
                                             Buf, num_read );
                if( ret_code != 0 ) {
                    // set failure status
                    parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
                    return HTTP_INTERNAL_SERVER_ERROR;
                }
                status = parser_parse_entity( parser );
                if( status == PARSE_INCOMPLETE_ENTITY ) {
                    // read until close
                    ok_on_close = TRUE;
                } else if( ( status != PARSE_SUCCESS )
                           && ( status != PARSE_CONTINUE_1 )
                           && ( status != PARSE_INCOMPLETE ) ) {
                    return HTTP_BAD_REQUEST;
                }
            } else if( num_read == 0 ) {
                if( ok_on_close ) {
                    UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
                        "<<< (RECVD) <<<\n%s\n-----------------\n",
                        parser->msg.msg.buf );
                    print_http_headers( &parser->msg );
                    parser->position = POS_COMPLETE;
                } else {
                    // partial msg
                    parser->http_error_code = HTTP_BAD_REQUEST; // or response
                    return HTTP_BAD_REQUEST;
                }
            } else {
                return num_read;
            }
        }

        if( ( entity_offset + Data_Buf_Size ) > parser->msg.entity.length ) {
            Data_Buf_Size = parser->msg.entity.length - entity_offset;
        }

        memcpy( Buf, &parser->msg.msg.buf[parser->entity_start_position
                                          + entity_offset],
                Data_Buf_Size );
        entity_offset += Data_Buf_Size;

        if( Instr->IsVirtualFile ) {
            Num_Write = virtualDirCallback.write( Fp, Buf, Data_Buf_Size );
            if( Num_Write < 0 ) {
                virtualDirCallback.close( Fp );
                return HTTP_INTERNAL_SERVER_ERROR;
            }
        } else {
            Num_Write = fwrite( Buf, 1, Data_Buf_Size, Fp );
            if( Num_Write < 0 ) {
                fclose( Fp );
                return HTTP_INTERNAL_SERVER_ERROR;
            }
        }

    } while( ( parser->position != POS_COMPLETE )
             || ( entity_offset != parser->msg.entity.length ) );

    if( Instr->IsVirtualFile ) {
        virtualDirCallback.close( Fp );
    } else {
        fclose( Fp );
    }

    /*
       while(TotalByteReceived < Instr->RecvWriteSize &&
       (NumReceived = sock_read(info,Buf, Data_Buf_Size,&Timeout) ) > 0 ) 
       {
       TotalByteReceived = TotalByteReceived + NumReceived;
       Num_Write = virtualDirCallback.write(Fp, Buf, NumReceived);
       if (ferror(Fp))
       {
       virtualDirCallback.close(Fp);
       return HTTP_INTERNAL_SERVER_ERROR;
       }
       }

       if(TotalByteReceived < Instr->RecvWriteSize)
       {
       return HTTP_INTERNAL_SERVER_ERROR;
       }

       virtualDirCallback.close(Fp);
       }
     */
    return HTTP_OK;
}

/************************************************************************
 * Function: web_server_callback
 *
 * Parameters:
 *	IN http_parser_t *parser ; HTTP Parser Object
 *	INOUT http_message_t* req ; HTTP Message request
 *	IN SOCKINFO *info ;	Socket information object
 *
 * Description: main entry point into web server;
 *	handles HTTP GET and HEAD requests
 *
 * Returns:
 *	void
 ************************************************************************/
void
web_server_callback( IN http_parser_t * parser,
                     INOUT http_message_t * req,
                     IN SOCKINFO * info )
{
    int ret;
    int timeout = 0;
    enum resp_type rtype = 0;
    membuffer headers;
    membuffer filename;
    struct xml_alias_t xmldoc;
    struct SendInstruction RespInstr;

    //Initialize instruction header.
    RespInstr.IsVirtualFile = 0;
    RespInstr.IsChunkActive = 0;
    RespInstr.IsRangeActive = 0;
    RespInstr.IsTrailers = 0;
    // init
    membuffer_init( &headers );
    membuffer_init( &filename );

    //Process request should create the different kind of header depending on the
    //the type of request.
    ret =
        process_request( req, &rtype, &headers, &filename, &xmldoc,
                         &RespInstr );
    if( ret != UPNP_E_SUCCESS ) {
        // send error code
        http_SendStatusResponse( info, ret, req->major_version,
                                 req->minor_version );
    } else {
        //
        // send response
        switch ( rtype ) {
            case RESP_FILEDOC: // send file, I = further instruction to send data.
                http_SendMessage( info, &timeout, "Ibf", &RespInstr,
                                  headers.buf, headers.length,
                                  filename.buf );
                break;

            case RESP_XMLDOC:  // send xmldoc , I = further instruction to send data.
                http_SendMessage( info, &timeout, "Ibb", &RespInstr,
                                  headers.buf, headers.length,
                                  xmldoc.doc.buf, xmldoc.doc.length );
                alias_release( &xmldoc );
                break;

            case RESP_WEBDOC:  //, I = further instruction to send data.
                /*
                   http_SendVirtualDirDoc( info, &timeout, "Ibf",&RespInstr,
                   headers.buf, headers.length,
                   filename.buf );  
                 */
                http_SendMessage( info, &timeout, "Ibf", &RespInstr,
                                  headers.buf, headers.length,
                                  filename.buf );
                break;

            case RESP_HEADERS: // headers only
                http_SendMessage( info, &timeout, "b",
                                  headers.buf, headers.length );

                break;
            case RESP_POST:    // headers only
                ret =
                    http_RecvPostMessage( parser, info, filename.buf,
                                          &RespInstr );
                //Send response.

                http_MakeMessage(
                    &headers, 1, 1,
                    "RTDSXcCc",
                    ret,
                    "text/html",
                    X_USER_AGENT );

                http_SendMessage( info, &timeout, "b", headers.buf,
                                  headers.length );
                break;

            default:
                assert( 0 );
        }
    }

    UpnpPrintf( UPNP_INFO, HTTP, __FILE__, __LINE__,
        "webserver: request processed...\n" );

    membuffer_destroy( &headers );
    membuffer_destroy( &filename );
}

