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