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