diff --git a/upnp/src/genlib/net/http/webserver.c b/upnp/src/genlib/net/http/webserver.c index bfaaf74..2be7128 100644 --- a/upnp/src/genlib/net/http/webserver.c +++ b/upnp/src/genlib/net/http/webserver.c @@ -29,16 +29,15 @@ * **************************************************************************/ - -/****************************************************************************** - * Purpose: This file defines the Web Server and has functions to carry out +/*! + * \file + * + * \brief Defines the Web Server and has functions to carry out * operations of the Web Server. - ******************************************************************************/ - + */ #include "config.h" - #include "webserver.h" @@ -56,482 +55,412 @@ #include "util.h" #include "VirtualDir.h" - #include #include #include - #ifdef UPNP_USE_BCBPP - /* Do not #include */ - /* Do not #include */ + /* Do not #include */ + /* Do not #include */ #else - #include - #include -#endif /* !UPNP_USE_BCBPP */ - + #include + #include +#endif /* !UPNP_USE_BCBPP */ #ifdef WIN32 - /* Do not #include */ + /* Do not #include */ #else - #include + #include #endif - -/* - Response Types +/*! + * Response Types. */ enum resp_type { RESP_FILEDOC, RESP_XMLDOC, RESP_HEADERS, RESP_WEBDOC, - RESP_POST }; + RESP_POST +}; -// mapping of file extension to content-type of document +/* 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; + /*! . */ + 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; + /*! name of DOC from root; e.g.: /foo/bar/mydesc.xml */ + membuffer name; + /*! the XML document contents */ + membuffer doc; + /*! . */ + time_t last_modified; + /*! . */ + int *ct; }; static const char *gMediaTypes[] = { - NULL, // 0 - "audio", // 1 - "video", // 2 - "image", // 3 - "application", // 4 - "text" // 5 + /*! 0. */ + NULL, + /*! 1. */ + "audio", + /*! 2. */ + "video", + /*! 3. */ + "image", + /*! 4. */ + "application", + /*! 5. */ + "text" }; /* - Defines + * Defines. */ -// index into 'gMediaTypes' +/* 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 +/* int index */ #define APPLICATION_INDEX 4 #define TEXT_INDEX 5 -// general +/* general */ #define NUM_MEDIA_TYPES 69 #define NUM_HTTP_HEADER_NAMES 33 -// sorted by file extension; must have 'NUM_MEDIA_TYPES' extensions +/* 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 *** + "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. + */ -/************************************************************************ - 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 + +/*! Global variable. A local dir which serves as webserver root. */ +membuffer gDocumentRootDir; + +/*! XML document. */ +static struct xml_alias_t gAliasDoc; 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 ) +/*! + * \brief Decodes list and stores it in gMediaTypeList. + */ +static UPNP_INLINE void media_list_init(void) { - int i; - const char *s = gEncodedMediaTypes; - struct document_type_t *doc_type; + 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 ); + for (i = 0; *s != '\0'; i++) { + doc_type = &gMediaTypeList[i]; + doc_type->file_ext = s; + /* point to type. */ + s += strlen(s) + 1; + doc_type->content_type = gMediaTypes[(int)*s]; + /* point to subtype. */ + s++; + doc_type->content_subtype = s; + /* next entry. */ + s += strlen(s) + 1; + } + assert(i == NUM_MEDIA_TYPES); } -/************************************************************************ - * Function: has_xml_content_type +/*! + * \brief Based on the extension, returns the content type and content + * subtype. * - * 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 ) + * \return + * \li \c 0 on success + * \li \c -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; + int top, mid, bot; + int cmp; - top = 0; - bot = NUM_MEDIA_TYPES - 1; + top = 0; + bot = NUM_MEDIA_TYPES - 1; - while( top <= bot ) { - mid = ( top + bot ) / 2; - cmp = strcasecmp( extension, gMediaTypeList[mid].file_ext ); + while (top <= bot) { + mid = (top + bot) / 2; + cmp = strcasecmp(extension, gMediaTypeList[mid].file_ext); + if (cmp > 0) { + /* look below mid. */ + top = mid + 1; + } else if (cmp < 0) { + /* look above mid. */ + bot = mid - 1; + } else { + /* cmp == 0 */ + *con_type = gMediaTypeList[mid].content_type; + *con_subtype = gMediaTypeList[mid].content_subtype; + return 0; + } + } - 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; + return -1; } -/************************************************************************ - * Function: get_content_type +/*! + * \brief Based on the extension, clones an XML string based on type and + * content subtype. If content type and sub type are not found, unknown + * types are used. * - * 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 - ************************************************************************/ -static UPNP_INLINE int -get_content_type( - IN const char *filename, + * \return + * \li \c 0 on success. + * \li \c UPNP_E_OUTOF_MEMORY - on memory allocation failures. + */ +static UPNP_INLINE int get_content_type( + /*! [in] . */ + const char *filename, + /*! [out] . */ OUT UpnpFileInfo *fileInfo) { - const char *extension; - const char *type; - const char *subtype; - xboolean ctype_found = FALSE; - char *temp = NULL; - int length = 0; + const char *extension; + const char *type; + const char *subtype; + xboolean ctype_found = FALSE; + char *temp = NULL; + int length = 0; - UpnpFileInfo_set_ContentType(fileInfo, NULL); + UpnpFileInfo_set_ContentType(fileInfo, NULL); - // get ext - extension = strrchr( filename, '.' ); - if (extension != NULL) { - if( search_extension( extension + 1, &type, &subtype ) == 0 ) { - ctype_found = TRUE; - } - } + // 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"; - } + 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); + 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; - } + if (!UpnpFileInfo_get_ContentType(fileInfo)) { + return UPNP_E_OUTOF_MEMORY; + } - return 0; + 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 ) +/*! + * \brief Initialize the global XML document. Allocate buffers for the XML + * document. + */ +static UPNP_INLINE void glob_alias_init(void) { - struct xml_alias_t *alias = &gAliasDoc; + struct xml_alias_t *alias = &gAliasDoc; - membuffer_init( &alias->doc ); - membuffer_init( &alias->name ); - alias->ct = NULL; - alias->last_modified = 0; + membuffer_init(&alias->doc); + membuffer_init(&alias->name); + alias->ct = NULL; + alias->last_modified = 0; } -/************************************************************************ - * Function: is_valid_alias +/*! + * \brief Check for the validity of the XML object buffer. * - * 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 BOOLEAN. + */ +static UPNP_INLINE xboolean is_valid_alias( + /*! [in] XML alias object. */ + const struct xml_alias_t *alias) { - return alias->doc.buf != NULL; + 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 ) +/*! + * \brief Copy the contents of the global XML document into the local output + * parameter. + */ +static void alias_grab( + /*! [out] XML alias object. */ + 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 ); + 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 ) +/*! + * \brief Release the XML document referred to by the input parameter. Free + * the allocated buffers associated with this object. + */ +static void alias_release( + /*! [in] XML alias object. */ + 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 ); + 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 web_server_set_alias(const char *alias_name, + const char *alias_content, size_t alias_content_length, + time_t last_modified) { - int ret_code; - struct xml_alias_t alias; + int ret_code; + struct xml_alias_t alias; - alias_release( &gAliasDoc ); + 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); - if( alias_name == NULL ) { - // don't serve aliased doc anymore - return 0; - } + return 0; + } while (FALSE); - 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; + // 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; @@ -560,58 +489,37 @@ int web_server_init() 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 ) +void web_server_destroy(void) { - int ret; + int ret; - if( bWebServerState == WEB_SERVER_ENABLED ) { - membuffer_destroy( &gDocumentRootDir ); - alias_release( &gAliasDoc ); + 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 ); + 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; - } + ret = ithread_mutex_destroy(&gWebMutex); + assert(ret == 0); + bWebServerState = WEB_SERVER_DISABLED; + } } -/************************************************************************ - * Function: get_file_info +/*! + * \brief Release memory allocated for the global web server root directory + * and the global XML document. Resets the flag bWebServerState to + * WEB_SERVER_DISABLED. * - * 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, + * \return Integer. + */ +static int get_file_info( + /*! [in] Filename having the description document. */ + const char *filename, + /*! [out] 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. */ OUT UpnpFileInfo *info) { int code; @@ -658,64 +566,42 @@ get_file_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 web_server_set_root_dir(const char *root_dir) { - int index; - int ret; + 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 ); - } - } + 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; + return 0; } -/************************************************************************ - * Function: get_alias +/*! + * \brief Compare the files names between the one on the XML alias the one + * passed in as the input parameter. If equal extract file information. * - * 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) + * \return + * \li \c TRUE - On Success + * \li \c FALSE if request is not an alias + */ +static UPNP_INLINE xboolean get_alias( + /*! [in] request file passed in to be compared with. */ + const char *request_file, + /*! [out] xml alias object which has a file name stored. */ + struct xml_alias_t *alias, + /*! [out] File information object which will be filled up if the file + * comparison succeeds. */ + UpnpFileInfo *info) { int cmp = strcmp(alias->name.buf, request_file); if (cmp == 0) { @@ -728,1060 +614,927 @@ get_alias( return cmp == 0; } -/************************************************************************ - * Function: isFileInVirtualDir +/*! + * \brief Compares filePath with paths from the list of virtual directory + * lists. * - * 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 ) + * \return BOOLEAN + */ +static int isFileInVirtualDir( + /*! [in] Directory path to be tested for virtual directory. */ + char *filePath) { - virtualDirList *pCurVirtualDir; - int webDirLen; + 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 = 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; + } - pCurVirtualDir = pCurVirtualDir->next; - } - - return FALSE; + 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 *s) +/*! + * \brief Converts input string to upper case. + */ +static void ToUpperCase( + /*! Input string to be converted. */ + char *s) { while (*s) { *s = toupper(*s); ++s; } +} + +/*! + * \brief Finds a substring from a string in a case insensitive way. + * + * \return A pointer to the first occurence of s2 in s1. + */ +static char *StrStr( + /*! Input string. */ + char *s1, + /*! Input sub-string. */ + char *s2) +{ + char *Str1; + char *Str2; + char *Ptr; + char *ret = NULL; + int Pos; + + Str1 = strdup(s1); + if (!Str1) + goto error1; + Str2 = strdup(s2); + if (!Str2) { + goto error2; + } + ToUpperCase(Str1); + ToUpperCase(Str2); + Ptr = strstr(Str1, Str2); + if (!Ptr) { + ret = NULL; + } else { + Pos = Ptr - Str1; + ret = s1 + Pos; + } + + free(Str2); +error2: + free(Str1); +error1: + return ret; +} + +/*! + * \brief Finds next token in a string. + * + * \return Pointer to the next token. + */ +static char *StrTok( + /*! String containing the token. */ + char **Src, + /*! Set of delimiter characters. */ + 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; +} + +/*! + * \brief Returns a range of integers from a string. + * + * \return Always returns 1. + */ +static int GetNextRange( + /*! string containing the token / range. */ + char **SrcRangeStr, + /*! gets the first byte of the token. */ + off_t *FirstByte, + /*! gets the last byte of the token. */ + 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: StrStr +/*! + * \brief Fills in the Offset, read size and contents to send out as an HTTP + * Range Response. * - * 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 ) + * \return + * \li \c HTTP_BAD_REQUEST + * \li \c UPNP_E_OUTOF_MEMORY + * \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE + * \li \c HTTP_OK + */ +static int CreateHTTPRangeResponseHeader( + /*! String containing the range. */ + char *ByteRangeSpecifier, + /*! Length of the file. */ + off_t FileLength, + /*! [out] SendInstruction object where the range operations will be stored. */ + struct SendInstruction *Instr) { - char *Str1, - *Str2; - char *Ptr, - *Ret; - int Pos; + off_t FirstByte, LastByte; + char *RangeInput, *Ptr; - 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 ) { - Ret = NULL; - } else { - Pos = Ptr - Str1; - Ret = S1 + Pos; - } - - free( Str1 ); - free( Str2 ); - return Ret; + 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; + /* Data between two range. */ + sprintf(Instr->RangeHeader, + "CONTENT-RANGE: bytes %" PRId64 + "-%" PRId64 "/%" PRId64 "\r\n", + (int64_t)FirstByte, + (int64_t)LastByte, + (int64_t)FileLength); + } 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: StrTok +/*! + * \brief Get header id from the request parameter and take appropriate + * action based on the ids as an HTTP Range Response. * - * 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 ) + * \return + * \li \c HTTP_BAD_REQUEST + * \li \c UPNP_E_OUTOF_MEMORY + * \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE + * \li \c HTTP_OK + */ +static int CheckOtherHTTPHeaders( + /*! [in] HTTP Request message. */ + http_message_t *Req, + /*! [out] Send Instruction object to data for the response. */ + struct SendInstruction *RespInstr, + /*! Size of the file containing the request document. */ + off_t FileSize) { - char *TmpPtr, - *RetPtr; + http_header_t *header; + ListNode *node; + //NNS: dlist_node* node; + int index, RetCode = HTTP_OK; + char *TmpBuf; - if( *Src != NULL ) { - RetPtr = *Src; - TmpPtr = strstr( *Src, Del ); - if( TmpPtr != NULL ) { - *TmpPtr = '\0'; - *Src = TmpPtr + strlen( Del ); - } else - *Src = NULL; + 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; - return RetPtr; - } + if (strlen(TmpBuf) > strlen("gzip")) { + /* means client will accept trailer. */ + if (StrStr(TmpBuf, "trailers") != NULL) { + RespInstr->IsTrailers = 1; + } + } + break; + } + case HDR_CONTENT_LENGTH: + RespInstr->RecvWriteSize = atoi(TmpBuf); + break; + case HDR_RANGE: + RetCode = CreateHTTPRangeResponseHeader(TmpBuf, + FileSize, RespInstr); + if (RetCode != HTTP_OK) { + free(TmpBuf); + return RetCode; + } + break; + case HDR_ACCEPT_LANGUAGE: + memcpy(RespInstr->AcceptLanguageHeader, TmpBuf, + sizeof(RespInstr->AcceptLanguageHeader) - 1); + 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_USER_AGENT: break;//return 1; + */ - return NULL; + //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: GetNextRange +/*! + * \brief Processes the request and returns the result in the output parameters. * - * 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 ) + * \return + * \li \c HTTP_BAD_REQUEST + * \li \c UPNP_E_OUTOF_MEMORY + * \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE + * \li \c HTTP_OK + */ +static int process_request( + /*! [in] HTTP Request message. */ + http_message_t *req, + /*! [out] Tpye of response. */ + enum resp_type *rtype, + /*! [out] Headers. */ + membuffer *headers, + /*! [out] Get filename from request document. */ + membuffer *filename, + /*! [out] Xml alias document from the request document. */ + struct xml_alias_t *alias, + /*! [out] Send Instruction object where the response is set up. */ + struct SendInstruction *RespInstr) { - char *Ptr; - char *Tok; - int i; - int64_t F = -1; - int64_t L = -1; - int Is_Suffix_byte_Range = 1; + int code; + int err_code; - if( *SrcRangeStr == NULL ) { - return -1; - } + 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 = NULL; - Tok = StrTok( SrcRangeStr, "," ); + 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; - if( ( Ptr = strstr( Tok, "-" ) ) == NULL ) { - return -1; - } - *Ptr = ' '; - sscanf( Tok, "%"SCNd64"%"SCNd64, &F, &L ); + 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; charset=\"utf-8\""); + 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 + // - 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; - } + // 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" "GKLD" "s" "tcS" "Xc" "sCc", + HTTP_PARTIAL_CONTENT, // status code + UpnpFileInfo_get_ContentType(finfo), // content type + RespInstr, // range info + RespInstr, // language 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" "GLD" "s" "tcS" "Xc" "sCc", + HTTP_PARTIAL_CONTENT, // status code + RespInstr->ReadSendSize, // content length + UpnpFileInfo_get_ContentType(finfo), // content type + RespInstr, // range info + RespInstr, // language 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" "TLD" "s" "tcS" "Xc" "sCc", + HTTP_OK, // status code + UpnpFileInfo_get_ContentType(finfo), // content type + RespInstr, // language info + "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" "TLD" "s" "tcS" "Xc" "sCc", + HTTP_OK, // status code + RespInstr->ReadSendSize, // content length + UpnpFileInfo_get_ContentType(finfo), // content type + RespInstr, // language info + "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" "TLD" "s" "tcS" "b" "Xc" "sCc", + HTTP_OK, // status code + UpnpFileInfo_get_ContentType(finfo), // content type + RespInstr, // language info + "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); + } - 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; + return err_code; } -/************************************************************************ - * Function: CreateHTTPRangeResponseHeader +/*! + * \brief Receives the HTTP post message. * - * 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 ) + * \return + * \li \c HTTP_INTERNAL_SERVER_ERROR + * \li \c HTTP_UNAUTHORIZED + * \li \c HTTP_REQUEST_RANGE_NOT_SATISFIABLE + * \li \c HTTP_OK + */ +static int http_RecvPostMessage( + /*! HTTP Parser object. */ + http_parser_t *parser, + /*! [in] Socket Information object. */ + SOCKINFO *info, + /*! File where received data is copied to. */ + char *filename, + /*! Send Instruction object which gives information whether the file + * is a virtual file or not. */ + 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; - off_t FirstByte, - LastByte; - char *RangeInput, - *Ptr; + 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 or response + parser->http_error_code = HTTP_BAD_REQUEST; + 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); + } - Instr->IsRangeActive = 1; - Instr->ReadSendSize = FileLength; + /* + 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( !ByteRangeSpecifier ) - return HTTP_BAD_REQUEST; + if(TotalByteReceived < Instr->RecvWriteSize) + { + return HTTP_INTERNAL_SERVER_ERROR; + } - 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; + virtualDirCallback.close(Fp); + } + */ + 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 ) +void web_server_callback(http_parser_t *parser, INOUT http_message_t *req, + SOCKINFO *info) { - http_header_t *header; - ListNode *node; + int ret; + int timeout = 0; + enum resp_type rtype = 0; + membuffer headers; + membuffer filename; + struct xml_alias_t xmldoc; + struct SendInstruction RespInstr; - //NNS: dlist_node* node; - int index, - RetCode = HTTP_OK; - char *TmpBuf; + //Initialize instruction header. + RespInstr.IsVirtualFile = 0; + RespInstr.IsChunkActive = 0; + RespInstr.IsRangeActive = 0; + RespInstr.IsTrailers = 0; + memset(RespInstr.AcceptLanguageHeader, 0, + sizeof(RespInstr.AcceptLanguageHeader)); + // init + membuffer_init(&headers); + membuffer_init(&filename); - 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; - case HDR_ACCEPT_LANGUAGE: - { - memcpy( RespInstr->AcceptLanguageHeader, TmpBuf, - sizeof( RespInstr->AcceptLanguageHeader ) - 1 ); - 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_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; charset=\"utf-8\""); - 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" "GKLD" "s" "tcS" "Xc" "sCc", - HTTP_PARTIAL_CONTENT, // status code - UpnpFileInfo_get_ContentType(finfo), // content type - RespInstr, // range info - RespInstr, // language 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" "GLD" "s" "tcS" "Xc" "sCc", - HTTP_PARTIAL_CONTENT, // status code - RespInstr->ReadSendSize, // content length - UpnpFileInfo_get_ContentType(finfo), // content type - RespInstr, // range info - RespInstr, // language 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" "TLD" "s" "tcS" "Xc" "sCc", - HTTP_OK, // status code - UpnpFileInfo_get_ContentType(finfo), // content type - RespInstr, // language info - "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" "TLD" "s" "tcS" "Xc" "sCc", - HTTP_OK, // status code - RespInstr->ReadSendSize, // content length - UpnpFileInfo_get_ContentType(finfo), // content type - RespInstr, // language info - "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" "TLD" "s" "tcS" "b" "Xc" "sCc", - HTTP_OK, // status code - UpnpFileInfo_get_ContentType(finfo), // content type - RespInstr, // language info - "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; - memset( RespInstr.AcceptLanguageHeader, 0, - sizeof( RespInstr.AcceptLanguageHeader ) ); - // 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, - "RTLSXcCc", - 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 ); + //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, + "RTLSXcCc", + 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); } diff --git a/upnp/src/inc/webserver.h b/upnp/src/inc/webserver.h index fae9944..f5e7840 100644 --- a/upnp/src/inc/webserver.h +++ b/upnp/src/inc/webserver.h @@ -1,33 +1,33 @@ -/******************************************************************************* +/************************************************************************** * - * Copyright (c) 2000-2003 Intel Corporation - * All rights reserved. + * 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: + * 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 + * - 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 + * + * 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 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - ******************************************************************************/ + **************************************************************************/ #ifndef GENLIB_NET_HTTP_WEBSERVER_H #define GENLIB_NET_HTTP_WEBSERVER_H @@ -50,98 +50,83 @@ struct SendInstruction char RangeHeader[200]; char AcceptLanguageHeader[200]; off_t RangeOffset; - off_t ReadSendSize; // Read from local source and send on the network. - long RecvWriteSize; // Recv from the network and write into local file. - - //Later few more member could be added depending on the requirement. + /*! Read from local source and send on the network. */ + off_t ReadSendSize; + /*! Recv from the network and write into local file. */ + long RecvWriteSize; + /* Later few more member could be added depending + * on the requirement.*/ }; -/************************************************************************ - * Function: web_server_init + +/*! + * \brief Initilialize the different documents. Initialize the memory + * for root directory for web server. Call to initialize global XML + * document. Sets bWebServerState to WEB_SERVER_ENABLED. * - * Parameters: - * none + * \note alias_content is not freed here * - * Description: Initilialize the different documents. Initialize the - * memory for root directory for web server. Call to initialize global - * XML document. Sets bWebServerState to WEB_SERVER_ENABLED - * - * Returns: - * 0 - OK - * UPNP_E_OUTOF_MEMORY: note: alias_content is not freed here - ************************************************************************/ + * \return + * \li \c 0 - OK + * \li \c UPNP_E_OUTOF_MEMORY + */ int web_server_init(void); -/************************************************************************ - * 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 - ************************************************************************/ + +/*! + * \brief Release memory allocated for the global web server root + * directory and the global XML document. Resets the flag bWebServerState + * to WEB_SERVER_DISABLED. + */ void web_server_destroy(void); -/************************************************************************ - * Function: web_server_set_alias + +/*! + * \brief Replaces current alias with the given alias. To remove the current + * alias, set alias_name to NULL. * - * 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) + * \note alias_content is not freed here * - * 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 - ************************************************************************/ + * \return + * \li \c 0 - OK + * \li \c UPNP_E_OUTOF_MEMORY + */ 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); + /*! [in] Webserver name of alias; created by caller and freed by caller + * (doesn't even have to be malloc()d. */ + const char* alias_name, + /*! [in] The xml doc; this is allocated by the caller; and freed by + * the web server. */ + const char* alias_content, + /*! [in] Length of alias body in bytes. */ + size_t alias_content_length, + /*! [in] Time when the contents of alias were last changed (local time). */ + time_t last_modified); -/************************************************************************ - * 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); -/************************************************************************ - * Function: web_server_callback +/*! + * \brief Assign the path specfied by the input const char* root_dir parameter + * to the global Document root directory. Also check for path names ending + * in '/'. * - * Parameters: - * IN http_parser_t *parser, - * INOUT http_message_t* req, - * IN SOCKINFO *info - * - * Description: main entry point into web server; - * handles HTTP GET and HEAD requests - * - * Returns: - * void - ************************************************************************/ -void web_server_callback(IN http_parser_t *parser, IN http_message_t *req, INOUT SOCKINFO *info); + * \return Integer. + */ +int web_server_set_root_dir( + /*! [in] String having the root directory for the document. */ + const char* root_dir); + + +/*! + * \brief Main entry point into web server; Handles HTTP GET and HEAD + * requests. + */ +void web_server_callback( + /*! [in] . */ + http_parser_t *parser, + /*! [in] . */ + http_message_t *req, + /*! [in,out] . */ + SOCKINFO *info); #ifdef __cplusplus