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 contains functions for scanner and parser for http
34* messages.
35************************************************************************/
36
37#include <assert.h>
38#include <string.h>
39#include <ctype.h>
40#include <limits.h>
41#include <stdarg.h>
42#include "strintmap.h"
43#include "httpparser.h"
44#include "statcodes.h"
45#include "unixutil.h"
46
47// entity positions
48
49#define NUM_HTTP_METHODS 9
50static str_int_entry Http_Method_Table[NUM_HTTP_METHODS] = {
51    {"GET", HTTPMETHOD_GET},
52    {"HEAD", HTTPMETHOD_HEAD},
53    {"M-POST", HTTPMETHOD_MPOST},
54    {"M-SEARCH", HTTPMETHOD_MSEARCH},
55    {"NOTIFY", HTTPMETHOD_NOTIFY},
56    {"POST", HTTPMETHOD_POST},
57    {"SUBSCRIBE", HTTPMETHOD_SUBSCRIBE},
58    {"UNSUBSCRIBE", HTTPMETHOD_UNSUBSCRIBE},
59    {"POST", SOAPMETHOD_POST},
60
61};
62
63#define NUM_HTTP_HEADER_NAMES 33
64str_int_entry Http_Header_Names[NUM_HTTP_HEADER_NAMES] = {
65    {"ACCEPT", HDR_ACCEPT},
66    {"ACCEPT-CHARSET", HDR_ACCEPT_CHARSET},
67    {"ACCEPT-ENCODING", HDR_ACCEPT_ENCODING},
68    {"ACCEPT-LANGUAGE", HDR_ACCEPT_LANGUAGE},
69    {"ACCEPT-RANGES", HDR_ACCEPT_RANGE},
70    {"CACHE-CONTROL", HDR_CACHE_CONTROL},
71    {"CALLBACK", HDR_CALLBACK},
72    {"CONTENT-ENCODING", HDR_CONTENT_ENCODING},
73    {"CONTENT-LANGUAGE", HDR_CONTENT_LANGUAGE},
74    {"CONTENT-LENGTH", HDR_CONTENT_LENGTH},
75    {"CONTENT-LOCATION", HDR_CONTENT_LOCATION},
76    {"CONTENT-RANGE", HDR_CONTENT_RANGE},
77    {"CONTENT-TYPE", HDR_CONTENT_TYPE},
78    {"DATE", HDR_DATE},
79    {"EXT", HDR_EXT},
80    {"HOST", HDR_HOST},
81    {"IF-RANGE", HDR_IF_RANGE},
82    {"LOCATION", HDR_LOCATION},
83    {"MAN", HDR_MAN},
84    {"MX", HDR_MX},
85    {"NT", HDR_NT},
86    {"NTS", HDR_NTS},
87    {"RANGE", HDR_RANGE},
88    {"SEQ", HDR_SEQ},
89    {"SERVER", HDR_SERVER},
90    {"SID", HDR_SID},
91    {"SOAPACTION", HDR_SOAPACTION},
92    {"ST", HDR_ST},
93    {"TE", HDR_TE},
94    {"TIMEOUT", HDR_TIMEOUT},
95    {"TRANSFER-ENCODING", HDR_TRANSFER_ENCODING},
96    {"USER-AGENT", HDR_USER_AGENT},
97    {"USN", HDR_USN}
98};
99
100/***********************************************************************/
101
102/*************				 scanner					  **************/
103
104/***********************************************************************/
105
106#define TOKCHAR_CR		0xD
107#define TOKCHAR_LF		0xA
108
109/************************************************************************
110*	Function :	scanner_init
111*
112*	Parameters :
113*		OUT scanner_t* scanner ; Scanner Object to be initialized
114*		IN membuffer* bufptr ;	 Buffer to be copied
115*
116*	Description :	Intialize scanner
117*
118*	Return : void ;
119*
120*	Note :
121************************************************************************/
122static XINLINE void
123scanner_init( OUT scanner_t * scanner,
124              IN membuffer * bufptr )
125{
126    scanner->cursor = 0;
127    scanner->msg = bufptr;
128    scanner->entire_msg_loaded = FALSE;
129}
130
131/************************************************************************
132*	Function :	is_separator_char
133*
134*	Parameters :
135*		IN char c ;	character to be tested against used separator values
136*
137*	Description :	Finds the separator character.
138*
139*	Return : xboolean ;
140*
141*	Note :
142************************************************************************/
143static XINLINE xboolean
144is_separator_char( IN char c )
145{
146    return strchr( " \t()<>@,;:\\\"/[]?={}", c ) != NULL;
147}
148
149/************************************************************************
150*	Function :	is_identifier_char
151*
152*	Parameters :
153*		IN char c ;	character to be tested for separator values
154*
155*	Description :	Calls the function to indentify separator character
156*
157*	Return : xboolean ;
158*
159*	Note :
160************************************************************************/
161static XINLINE xboolean
162is_identifier_char( IN char c )
163{
164    return ( c >= 32 && c <= 126 ) && !is_separator_char( c );
165}
166
167/************************************************************************
168*	Function :	is_control_char
169*
170*	Parameters :
171*		IN char c ;	character to be tested for a control character
172*
173*	Description :	Determines if the passed value is a control character
174*
175*	Return : xboolean ;
176*
177*	Note :
178************************************************************************/
179static XINLINE xboolean
180is_control_char( IN char c )
181{
182    return ( ( c >= 0 && c <= 31 ) || ( c == 127 ) );
183}
184
185/************************************************************************
186*	Function :	is_qdtext_char
187*
188*	Parameters :
189*		IN char cc ; character to be tested for CR/LF
190*
191*	Description :	Checks to see if the passed in value is CR/LF
192*
193*	Return : xboolean ;
194*
195*	Note :
196************************************************************************/
197static XINLINE xboolean
198is_qdtext_char( IN char cc )
199{
200    unsigned char c = ( unsigned char )cc;
201
202    // we don't check for this; it's checked in get_token()
203    assert( c != '"' );
204
205    if( ( c >= 32 && c != 127 ) ||
206        ( c == TOKCHAR_CR || c == TOKCHAR_LF || c == '\t' )
207         ) {
208        return TRUE;
209    } else {
210        return FALSE;
211    }
212}
213
214/************************************************************************
215*	Function :	scanner_get_token
216*
217*	Parameters :
218*		INOUT scanner_t* scanner ;	Scanner Object
219*		OUT memptr* token ;			Token
220*		OUT token_type_t* tok_type ; Type of token
221*
222*	Description :	reads next token from the input stream
223*		note: 0 and is used as a marker, and will not be valid in a quote
224*
225*	Return : parse_status_t ;
226*		PARSE_OK
227*		PARSE_INCOMPLETE		-- not enuf chars to get a token
228*		PARSE_FAILURE			-- bad msg format
229*
230*	Note :
231************************************************************************/
232static parse_status_t
233scanner_get_token( INOUT scanner_t * scanner,
234                   OUT memptr * token_1,
235                   OUT token_type_t * tok_type )
236{
237    char *cursor;
238    char *null_terminator;      // point to null-terminator in buffer
239    char c;
240    token_type_t token_type;
241    xboolean got_end_quote;
242
243    assert( scanner );
244    assert( token_1 );
245    assert( tok_type );
246
247    // point to next char in buffer
248    cursor = scanner->msg->buf + scanner->cursor;
249    null_terminator = scanner->msg->buf + scanner->msg->length;
250
251    // not enough chars in input to parse
252    if( cursor == null_terminator ) {
253        return PARSE_INCOMPLETE;
254    }
255
256    c = *cursor;
257    if( is_identifier_char( c ) ) {
258        // scan identifier
259
260        token_1->buf = cursor++;
261        token_type = TT_IDENTIFIER;
262
263        while( is_identifier_char( *cursor ) ) {
264            cursor++;
265        }
266
267        if( !scanner->entire_msg_loaded && cursor == null_terminator ) {
268            // possibly more valid chars
269            return PARSE_INCOMPLETE;
270        }
271        // calc token length
272        token_1->length = cursor - token_1->buf;
273    } else if( c == ' ' || c == '\t' ) {
274        token_1->buf = cursor++;
275        token_type = TT_WHITESPACE;
276
277        while( *cursor == ' ' || *cursor == '\t' ) {
278            cursor++;
279        }
280
281        if( !scanner->entire_msg_loaded && cursor == null_terminator ) {
282            // possibly more chars
283            return PARSE_INCOMPLETE;
284        }
285        token_1->length = cursor - token_1->buf;
286    } else if( c == TOKCHAR_CR ) {
287        // scan CRLF
288
289        token_1->buf = cursor++;
290        if( cursor == null_terminator ) {
291            // not enuf info to determine CRLF
292            return PARSE_INCOMPLETE;
293        }
294        if( *cursor != TOKCHAR_LF ) {
295            // couldn't match CRLF; match as CR
296            token_type = TT_CTRL;   // ctrl char
297            token_1->length = 1;
298        } else {
299            // got CRLF
300            token_1->length = 2;
301            token_type = TT_CRLF;
302            cursor++;
303        }
304    } else if( c == TOKCHAR_LF )    // accept \n as CRLF
305    {
306        token_1->buf = cursor++;
307        token_1->length = 1;
308        token_type = TT_CRLF;
309    } else if( c == '"' ) {
310        // quoted text
311        token_1->buf = cursor++;
312        token_type = TT_QUOTEDSTRING;
313        got_end_quote = FALSE;
314
315        while( cursor < null_terminator ) {
316            c = *cursor++;
317            if( c == '"' ) {
318                got_end_quote = TRUE;
319                break;
320            } else if( c == '\\' ) {
321                if( cursor < null_terminator ) {
322                    c = *cursor++;
323                    //if ( !(c > 0 && c <= 127) )
324                    if( c == 0 ) {
325                        return PARSE_FAILURE;
326                    }
327                }
328                // else, while loop handles incomplete buf
329            } else if( is_qdtext_char( c ) ) {
330                // just accept char
331            } else {
332                // bad quoted text
333                return PARSE_FAILURE;
334            }
335        }
336        if( got_end_quote ) {
337            token_1->length = cursor - token_1->buf;
338        } else                  // incomplete
339        {
340            assert( cursor == null_terminator );
341            return PARSE_INCOMPLETE;
342        }
343    } else if( is_separator_char( c ) ) {
344        // scan separator
345
346        token_1->buf = cursor++;
347        token_type = TT_SEPARATOR;
348        token_1->length = 1;
349    } else if( is_control_char( c ) ) {
350        // scan ctrl char
351
352        token_1->buf = cursor++;
353        token_type = TT_CTRL;
354        token_1->length = 1;
355    } else {
356        return PARSE_FAILURE;
357    }
358
359    scanner->cursor += token_1->length;   // move to next token
360    *tok_type = token_type;
361    return PARSE_OK;
362}
363
364/************************************************************************
365*	Function :	scanner_get_str
366*
367*	Parameters :
368*		IN scanner_t* scanner ;	Scanner Object
369*
370*	Description :	returns ptr to next char in string
371*
372*	Return : char* ;
373*
374*	Note :
375************************************************************************/
376static XINLINE char *
377scanner_get_str( IN scanner_t * scanner )
378{
379    return scanner->msg->buf + scanner->cursor;
380}
381
382/************************************************************************
383*	Function :	scanner_pushback
384*
385*	Parameters :
386*		INOUT scanner_t* scanner ;	Scanner Object
387*		IN size_t pushback_bytes ;	Bytes to be moved back
388*
389*	Description :	Move back by a certain number of bytes.
390*		This is used to put back one or more tokens back into the input
391*
392*	Return : void ;
393*
394*	Note :
395************************************************************************/
396static XINLINE void
397scanner_pushback( INOUT scanner_t * scanner,
398                  IN size_t pushback_bytes )
399{
400    scanner->cursor -= pushback_bytes;
401}
402
403/***********************************************************************/
404
405/*************				end of scanner				  **************/
406
407/***********************************************************************/
408
409/***********************************************************************/
410
411/*************					parser					  **************/
412
413/***********************************************************************/
414
415/***********************************************************************/
416
417/*************				  http_message_t			  **************/
418
419/***********************************************************************/
420
421/************************************************************************
422*	Function :	httpmsg_compare
423*
424*	Parameters :
425*		void* param1 ;
426*		void* param2 ;
427*
428*	Description :	Compares name id in the http headers.
429*
430*	Return : int ;
431*
432*	Note :
433************************************************************************/
434static int
435httpmsg_compare( void *param1,
436                 void *param2 )
437{
438    assert( param1 != NULL );
439    assert( param2 != NULL );
440
441    return ( ( http_header_t * ) param1 )->name_id ==
442        ( ( http_header_t * ) param2 )->name_id;
443}
444
445/************************************************************************
446*	Function :	httpheader_free
447*
448*	Parameters :
449*		void *msg ;
450*
451*	Description :	Free memory allocated for the http header
452*
453*	Return : void ;
454*
455*	Note :
456************************************************************************/
457static void
458httpheader_free( void *msg )
459{
460    http_header_t *hdr = ( http_header_t * ) msg;
461
462	if(hdr==NULL)
463		return;
464    membuffer_destroy( &hdr->name_buf );
465    membuffer_destroy( &hdr->value );
466    free( hdr );
467}
468
469/************************************************************************
470*	Function :	httpmsg_init
471*
472*	Parameters :
473*		INOUT http_message_t* msg ;	HTTP Message Object
474*
475*	Description :	Initialize and allocate memory for http message
476*
477*	Return : void ;
478*
479*	Note :
480************************************************************************/
481void
482httpmsg_init( INOUT http_message_t * msg )
483{
484    msg->initialized = 1;
485    msg->entity.buf = NULL;
486    msg->entity.length = 0;
487    ListInit( &msg->headers, httpmsg_compare, httpheader_free );
488    membuffer_init( &msg->msg );
489    membuffer_init( &msg->status_msg );
490}
491
492/************************************************************************
493*	Function :	httpmsg_destroy
494*
495*	Parameters :
496*		INOUT http_message_t* msg ;	HTTP Message Object
497*
498*	Description :	Free memory allocated for the http message
499*
500*	Return : void ;
501*
502*	Note :
503************************************************************************/
504void
505httpmsg_destroy( INOUT http_message_t * msg )
506{
507    assert( msg != NULL );
508
509    if( msg->initialized == 1 ) {
510        ListDestroy( &msg->headers, 1 );
511        membuffer_destroy( &msg->msg );
512        membuffer_destroy( &msg->status_msg );
513        if(msg->urlbuf)
514        	free( msg->urlbuf );
515        msg->initialized = 0;
516    }
517}
518
519/************************************************************************
520*	Function :	httpmsg_find_hdr_str
521*
522*	Parameters :
523*		IN http_message_t* msg ;	HTTP Message Object
524*		IN const char* header_name ; Header name to be compared with
525*
526*	Description :	Compares the header name with the header names stored
527*		in	the linked list of messages
528*
529*	Return : http_header_t* - Pointer to a header on success;
530*			 NULL on failure
531*
532*	Note :
533************************************************************************/
534http_header_t *
535httpmsg_find_hdr_str( IN http_message_t * msg,
536                      IN const char *header_name )
537{
538    http_header_t *header;
539
540    ListNode *node;
541
542    node = ListHead( &msg->headers );
543    while( node != NULL ) {
544
545        header = ( http_header_t * ) node->item;
546
547        if( memptr_cmp_nocase( &header->name, header_name ) == 0 ) {
548            return header;
549        }
550
551        node = ListNext( &msg->headers, node );
552    }
553    return NULL;
554}
555
556/************************************************************************
557*	Function :	httpmsg_find_hdr
558*
559*	Parameters :
560*		IN http_message_t* msg ; HTTP Message Object
561*		IN int header_name_id ;	 Header Name ID to be compared with
562*		OUT memptr* value ;		 Buffer to get the ouput to.
563*
564*	Description :	Finds header from a list, with the given 'name_id'.
565*
566*	Return : http_header_t*  - Pointer to a header on success;										*
567*			 NULL on failure
568*
569*	Note :
570************************************************************************/
571http_header_t *
572httpmsg_find_hdr( IN http_message_t * msg,
573                  IN int header_name_id,
574                  OUT memptr * value )
575{
576    http_header_t header;       // temp header for searching
577
578    ListNode *node;
579
580    http_header_t *data;
581
582    header.name_id = header_name_id;
583
584    node = ListFind( &msg->headers, NULL, &header );
585
586    if( node == NULL ) {
587        return NULL;
588    }
589
590    data = ( http_header_t * ) node->item;
591
592    if( value != NULL ) {
593        value->buf = data->value.buf;
594        value->length = data->value.length;
595    }
596
597    return data;
598}
599
600/***********************************************************************/
601
602/*************				  http_parser_t				  **************/
603
604/***********************************************************************/
605
606/************************************************************************
607*	Function :	skip_blank_lines
608*
609*	Parameters :
610*		INOUT scanner_t* scanner ;	Scanner Object
611*
612*	Description :	skips blank lines at the start of a msg.
613*
614*	Return : int ;
615*
616*	Note :
617************************************************************************/
618static XINLINE int
619skip_blank_lines( INOUT scanner_t * scanner )
620{
621    memptr token_1;
622    token_type_t tok_type;
623    parse_status_t status;
624
625    // skip ws, crlf
626    do {
627        status = scanner_get_token( scanner, &token_1, &tok_type );
628    } while( status == PARSE_OK &&
629             ( tok_type == TT_WHITESPACE || tok_type == TT_CRLF ) );
630
631    if( status == PARSE_OK ) {
632        // pushback a non-whitespace token
633        scanner->cursor -= token_1.length;
634        //scanner_pushback( scanner, token.length );
635    }
636
637    return status;
638}
639
640/************************************************************************
641*	Function :	skip_lws
642*
643*	Parameters :
644*		INOUT scanner_t* scanner ;	Scanner Object
645*
646*	Description :	skip linear whitespace.
647*
648*	Return : int ;
649*		PARSE_OK: (LWS)* removed from input
650*		PARSE_FAILURE: bad input
651*		PARSE_INCOMPLETE: incomplete input
652*
653*	Note :
654************************************************************************/
655static XINLINE int
656skip_lws( INOUT scanner_t * scanner )
657{
658    memptr token_1;
659    token_type_t tok_type;
660    parse_status_t status;
661    size_t save_pos;
662    xboolean matched;
663
664    do {
665        save_pos = scanner->cursor;
666        matched = FALSE;
667
668        // get CRLF or WS
669        status = scanner_get_token( scanner, &token_1, &tok_type );
670        if( status == PARSE_OK ) {
671            if( tok_type == TT_CRLF ) {
672                // get WS
673                status = scanner_get_token( scanner, &token_1, &tok_type );
674            }
675
676            if( status == PARSE_OK && tok_type == TT_WHITESPACE ) {
677                matched = TRUE;
678            } else {
679                // did not match LWS; pushback token(s)
680                scanner->cursor = save_pos;
681            }
682        }
683    } while( matched );
684
685    // if entire msg is loaded, ignore an 'incomplete' warning
686    if( status == PARSE_INCOMPLETE && scanner->entire_msg_loaded ) {
687        status = PARSE_OK;
688    }
689
690    return status;
691}
692
693/************************************************************************
694*	Function :	match_non_ws_string
695*
696*	Parameters :
697*		INOUT scanner_t* scanner ;	Scanner Object
698*		OUT memptr* str ;	Buffer to get the scanner buffer contents.
699*
700*	Description :	Match a string without whitespace or CRLF (%S)
701*
702*	Return : XINLINE parse_status_t ;
703*		PARSE_OK
704*		PARSE_NO_MATCH
705*		PARSE_FAILURE
706*		PARSE_INCOMPLETE
707*
708*	Note :
709************************************************************************/
710static XINLINE parse_status_t
711match_non_ws_string( INOUT scanner_t * scanner,
712                     OUT memptr * str )
713{
714    memptr token_1;
715    token_type_t tok_type;
716    parse_status_t status;
717    xboolean done = FALSE;
718    size_t save_cursor;
719
720    save_cursor = scanner->cursor;
721
722    str->length = 0;
723    str->buf = scanner_get_str( scanner );  // point to next char in input
724
725    while( !done ) {
726        status = scanner_get_token( scanner, &token_1, &tok_type );
727        if( status == PARSE_OK &&
728            tok_type != TT_WHITESPACE && tok_type != TT_CRLF ) {
729            // append non-ws token
730            str->length += token_1.length;
731        } else {
732            done = TRUE;
733        }
734    }
735
736    if( status == PARSE_OK ) {
737        // last token was WS; push it back in
738        scanner->cursor -= token_1.length;
739    }
740    // tolerate 'incomplete' msg
741    if( status == PARSE_OK ||
742        ( status == PARSE_INCOMPLETE && scanner->entire_msg_loaded )
743         ) {
744        if( str->length == 0 ) {
745            // no strings found
746            return PARSE_NO_MATCH;
747        } else {
748            return PARSE_OK;
749        }
750    } else {
751        // error -- pushback tokens
752        scanner->cursor = save_cursor;
753        return status;
754    }
755}
756
757/************************************************************************
758*	Function :	match_raw_value
759*
760*	Parameters :
761*		INOUT scanner_t* scanner ;	Scanner Object
762*		OUT memptr* raw_value ;	    Buffer to get the scanner buffer
763*									contents
764*
765*	Description :	Matches a raw value in a the input; value's length
766*		can	be 0 or more. Whitespace after value is trimmed. On success,
767*		scanner points the CRLF that ended the value
768*
769*	Return : parse_status_t ;
770*		PARSE_OK
771*		PARSE_INCOMPLETE
772*		PARSE_FAILURE
773*
774*	Note :
775************************************************************************/
776static XINLINE parse_status_t
777match_raw_value( INOUT scanner_t * scanner,
778                 OUT memptr * raw_value )
779{
780    memptr token_1;
781    token_type_t tok_type;
782    parse_status_t status;
783    xboolean done = FALSE;
784    xboolean saw_crlf = FALSE;
785    size_t pos_at_crlf = 0;
786    size_t save_pos;
787    char c;
788
789    save_pos = scanner->cursor;
790
791    // value points to start of input
792    raw_value->buf = scanner_get_str( scanner );
793    raw_value->length = 0;
794
795    while( !done ) {
796        status = scanner_get_token( scanner, &token_1, &tok_type );
797        if( status == PARSE_OK ) {
798            if( !saw_crlf ) {
799                if( tok_type == TT_CRLF ) {
800                    // CRLF could end value
801                    saw_crlf = TRUE;
802
803                    // save input position at start of CRLF
804                    pos_at_crlf = scanner->cursor - token_1.length;
805                }
806                // keep appending value
807                raw_value->length += token_1.length;
808            } else              // already seen CRLF
809            {
810                if( tok_type == TT_WHITESPACE ) {
811                    // start again; forget CRLF
812                    saw_crlf = FALSE;
813                    raw_value->length += token_1.length;
814                } else {
815                    // non-ws means value ended just before CRLF
816                    done = TRUE;
817
818                    // point to the crlf which ended the value
819                    scanner->cursor = pos_at_crlf;
820                }
821            }
822        } else {
823            // some kind of error; restore scanner position
824            scanner->cursor = save_pos;
825            done = TRUE;
826        }
827    }
828
829    if( status == PARSE_OK ) {
830        // trim whitespace on right side of value
831        while( raw_value->length > 0 ) {
832            // get last char
833            c = raw_value->buf[raw_value->length - 1];
834
835            if( c != ' ' && c != '\t' &&
836                c != TOKCHAR_CR && c != TOKCHAR_LF ) {
837                // done; no more whitespace
838                break;
839            }
840            // remove whitespace
841            raw_value->length--;
842        }
843    }
844
845    return status;
846}
847
848/************************************************************************
849* Function: match_int
850*
851* Parameters:
852*	INOUT scanner_t* scanner ;  Scanner Object
853*	IN int base :				Base of number in the string;
854*								valid values: 10 or 16
855*	OUT int* value ;			Number stored here
856*
857* Description: Matches an unsigned integer value in the input. The
858*	integer is returned in 'value'. Except for PARSE_OK result, the
859*	scanner's cursor is moved back to its original position on error.
860*
861* Returns:
862*   PARSE_OK
863*   PARSE_NO_MATCH		-- got different kind of token
864*   PARSE_FAILURE		-- bad input
865*   PARSE_INCOMPLETE
866************************************************************************/
867static XINLINE int
868match_int( INOUT scanner_t * scanner,
869           IN int base,
870           OUT int *value )
871{
872    memptr token_1;
873    token_type_t tok_type;
874    parse_status_t status;
875    int num;
876    char *end_ptr;
877    size_t save_pos;
878
879    save_pos = scanner->cursor;
880
881    status = scanner_get_token( scanner, &token_1, &tok_type );
882    if( status == PARSE_OK ) {
883        if( tok_type == TT_IDENTIFIER ) {
884            errno = 0;
885
886            num = strtol( token_1.buf, &end_ptr, base );
887            if( ( num < 0 )
888                // all and only those chars in token should be used for num
889                || ( end_ptr != token_1.buf + token_1.length )
890                || ( ( num == LONG_MIN || num == LONG_MAX )
891                     && ( errno == ERANGE ) )
892                 ) {
893                status = PARSE_NO_MATCH;
894            }
895
896            *value = num;       // save result
897        } else {
898            status = PARSE_NO_MATCH;    // token must be an identifier
899        }
900    }
901
902    if( status != PARSE_OK ) {
903        // restore scanner position for bad values
904        scanner->cursor = save_pos;
905    }
906
907    return status;
908}
909
910/************************************************************************
911* Function: read_until_crlf
912*
913* Parameters:
914*	INOUT scanner_t* scanner ;	Scanner Object
915*	OUT memptr* str ;			Buffer to copy scanner buffer contents to
916*
917* Description: Reads data until end of line; the crlf at the end of
918*	line is not consumed. On error, scanner is not restored. On
919*	success, 'str' points to a string that runs until eol
920*
921* Returns:
922*   PARSE_OK
923*   PARSE_FAILURE
924*   PARSE_INCOMPLETE
925************************************************************************/
926static XINLINE int
927read_until_crlf( INOUT scanner_t * scanner,
928                 OUT memptr * str )
929{
930    memptr token_1;
931    token_type_t tok_type;
932    parse_status_t status;
933    size_t start_pos;
934
935    start_pos = scanner->cursor;
936    str->buf = scanner_get_str( scanner );
937
938    // read until we hit a crlf
939    do {
940        status = scanner_get_token( scanner, &token_1, &tok_type );
941    } while( status == PARSE_OK && tok_type != TT_CRLF );
942
943    if( status == PARSE_OK ) {
944        // pushback crlf in stream
945        scanner->cursor -= token_1.length;
946
947        // str should include all strings except crlf at the end
948        str->length = scanner->cursor - start_pos;
949    }
950
951    return status;
952}
953
954/************************************************************************
955* Function: skip_to_end_of_header
956*
957* Parameters:
958*	INOUT scanner_t* scanner ; Scanner Object
959*
960* Description: Skip to end of header
961*
962* Returns:
963*   PARSE_OK
964*   PARSE_FAILURE
965*   PARSE_INCOMPLETE
966************************************************************************/
967static XINLINE int
968skip_to_end_of_header( INOUT scanner_t * scanner )
969{
970    memptr dummy_raw_value;
971    parse_status_t status;
972
973    status = match_raw_value( scanner, &dummy_raw_value );
974    return status;
975}
976
977/************************************************************************
978* Function: match_char
979*
980* Parameters:
981*	INOUT scanner_t* scanner ;  Scanner Object
982*	IN char c ;					Character to be compared with
983*	IN xboolean case_sensitive; Flag indicating whether comparison should
984*								be case sensitive
985*
986* Description: Compares a character to the next char in the scanner;
987*	on error, scanner chars are not restored
988*
989* Returns:
990*   PARSE_OK
991*   PARSE_NO_MATCH
992*   PARSE_INCOMPLETE
993************************************************************************/
994static XINLINE parse_status_t
995match_char( INOUT scanner_t * scanner,
996            IN char c,
997            IN xboolean case_sensitive )
998{
999    char scan_char;
1000
1001    if( scanner->cursor >= scanner->msg->length ) {
1002        return PARSE_INCOMPLETE;
1003    }
1004    // read next char from scanner
1005    scan_char = scanner->msg->buf[scanner->cursor++];
1006
1007    if( case_sensitive ) {
1008        return c == scan_char ? PARSE_OK : PARSE_NO_MATCH;
1009    } else {
1010        return tolower( c ) == tolower( scan_char ) ?
1011            PARSE_OK : PARSE_NO_MATCH;
1012    }
1013}
1014
1015////////////////////////////////////////////////////////////////////////
1016// args for ...
1017//   %d,    int *     (31-bit positive integer)
1018//   %x,    int *     (31-bit postive number encoded as hex)
1019//   %s,    memptr*  (simple identifier)
1020//   %q,    memptr*  (quoted string)
1021//   %S,    memptr*  (non-whitespace string)
1022//   %R,    memptr*  (raw value)
1023//   %U,    uri_type* (url)
1024//   %L,    memptr*  (string until end of line)
1025//   %P,    int * (current index of the string being scanned)
1026//
1027// no args for
1028//   ' '    LWS*
1029//   \t     whitespace
1030//   "%%"   matches '%'
1031//   "% "   matches ' '
1032//   %c     matches CRLF
1033//   %i     ignore case in literal matching
1034//   %n     case-sensitive matching in literals
1035//   %w     optional whitespace; (similar to '\t',
1036//                  except whitespace is optional)
1037//   %0     (zero) match null-terminator char '\0'
1038//              (can only be used as last char in fmt)
1039//              use only in matchstr(), not match()
1040//   other chars match literally
1041//
1042// returns:
1043//   PARSE_OK
1044//   PARSE_INCOMPLETE
1045//   PARSE_FAILURE      -- bad input
1046//   PARSE_NO_MATCH     -- input does not match pattern
1047
1048/************************************************************************
1049*	Function :	vfmatch
1050*
1051*	Parameters :
1052*		INOUT scanner_t* scanner ;  Scanner Object
1053*		IN const char* fmt ;		Pattern Format
1054*		va_list argp ;				List of variable arguments
1055*
1056*	Description :	Extracts variable parameters depending on the passed
1057*		in format parameter. Parses data also based on the passed in
1058*		format parameter.
1059*
1060*	Return : int ;
1061*		PARSE_OK
1062*		PARSE_INCOMPLETE
1063*		PARSE_FAILURE		- bad input
1064*		PARSE_NO_MATCH		- input does not match pattern
1065*
1066*	Note :
1067************************************************************************/
1068static int
1069vfmatch( INOUT scanner_t * scanner,
1070         IN const char *fmt,
1071         va_list argp )
1072{
1073    char c;
1074    const char *fmt_ptr = fmt;
1075    parse_status_t status;
1076    memptr *str_ptr;
1077    memptr temp_str;
1078    int *int_ptr;
1079    uri_type *uri_ptr;
1080    size_t save_pos;
1081    int stat;
1082    xboolean case_sensitive = TRUE;
1083    memptr token_1;
1084    token_type_t tok_type;
1085    int base;
1086
1087    assert( scanner != NULL );
1088    assert( fmt != NULL );
1089
1090    // save scanner pos; to aid error recovery
1091    save_pos = scanner->cursor;
1092
1093    status = PARSE_OK;
1094    while( ( ( c = *fmt_ptr++ ) != 0 ) && ( status == PARSE_OK )
1095         ) {
1096        if( c == '%' ) {
1097            c = *fmt_ptr++;
1098
1099            switch ( c ) {
1100
1101                case 'R':      // raw value
1102                    str_ptr = va_arg( argp, memptr * );
1103                    assert( str_ptr != NULL );
1104                    status = match_raw_value( scanner, str_ptr );
1105                    break;
1106
1107                case 's':      // simple identifier
1108                    str_ptr = va_arg( argp, memptr * );
1109                    assert( str_ptr != NULL );
1110                    status = scanner_get_token( scanner, str_ptr,
1111                                                &tok_type );
1112                    if( status == PARSE_OK && tok_type != TT_IDENTIFIER ) {
1113                        // not an identifier
1114                        status = PARSE_NO_MATCH;
1115                    }
1116                    break;
1117
1118                case 'c':      // crlf
1119                    status = scanner_get_token( scanner,
1120                                                &token_1, &tok_type );
1121                    if( status == PARSE_OK && tok_type != TT_CRLF ) {
1122                        // not CRLF token
1123                        status = PARSE_NO_MATCH;
1124                    }
1125                    break;
1126
1127                case 'd':      // integer
1128                case 'x':      // hex number
1129                    int_ptr = va_arg( argp, int * );
1130
1131                    assert( int_ptr != NULL );
1132                    base = ( c == 'd' ? 10 : 16 );
1133                    status = match_int( scanner, base, int_ptr );
1134                    break;
1135
1136                case 'S':      // non-whitespace string
1137                case 'U':      // uri
1138                    if( c == 'S' ) {
1139                        str_ptr = va_arg( argp, memptr * );
1140                    } else {
1141                        str_ptr = &temp_str;
1142                    }
1143                    assert( str_ptr != NULL );
1144                    status = match_non_ws_string( scanner, str_ptr );
1145                    if( c == 'U' && status == PARSE_OK ) {
1146                        uri_ptr = va_arg( argp, uri_type * );
1147                        assert( uri_ptr != NULL );
1148                        stat = parse_uri( str_ptr->buf, str_ptr->length,
1149                                          uri_ptr );
1150                        if( stat != HTTP_SUCCESS ) {
1151                            status = PARSE_NO_MATCH;
1152                        }
1153                    }
1154                    break;
1155
1156                case 'L':      // string till eol
1157                    str_ptr = va_arg( argp, memptr * );
1158                    assert( str_ptr != NULL );
1159                    status = read_until_crlf( scanner, str_ptr );
1160                    break;
1161
1162                case ' ':      // match space
1163                case '%':      // match percentage symbol
1164                    status = match_char( scanner, c, case_sensitive );
1165                    break;
1166
1167                case 'n':      // case-sensitive match
1168                    case_sensitive = TRUE;
1169                    break;
1170
1171                case 'i':      // ignore case
1172                    case_sensitive = FALSE;
1173                    break;
1174
1175                case 'q':      // quoted string
1176                    str_ptr = ( memptr * ) va_arg( argp, memptr * );
1177                    status =
1178                        scanner_get_token( scanner, str_ptr, &tok_type );
1179                    if( status == PARSE_OK && tok_type != TT_QUOTEDSTRING ) {
1180                        status = PARSE_NO_MATCH;    // not a quoted string
1181                    }
1182                    break;
1183
1184                case 'w':      // optional whitespace
1185                    status = scanner_get_token( scanner,
1186                                                &token_1, &tok_type );
1187                    if( status == PARSE_OK && tok_type != TT_WHITESPACE ) {
1188                        // restore non-whitespace token
1189                        scanner->cursor -= token_1.length;
1190                    }
1191                    break;
1192
1193                case 'P':      // current pos of scanner
1194                    int_ptr = va_arg( argp, int * );
1195
1196                    assert( int_ptr != NULL );
1197                    *int_ptr = scanner->cursor;
1198                    break;
1199
1200                    // valid only in matchstr()
1201                case '0':      // end of msg?
1202                    // check that we are 1 beyond last char
1203                    if( scanner->cursor == scanner->msg->length &&
1204                        scanner->msg->buf[scanner->cursor] == '\0' ) {
1205                        status = PARSE_OK;
1206                    } else {
1207                        status = PARSE_NO_MATCH;
1208                    }
1209                    break;
1210
1211                default:
1212                    assert( 0 );    // unknown option
1213            }
1214        } else {
1215            switch ( c ) {
1216                case ' ':      // LWS*
1217                    status = skip_lws( scanner );
1218                    break;
1219
1220                case '\t':     // Whitespace
1221                    status = scanner_get_token( scanner,
1222                                                &token_1, &tok_type );
1223                    if( status == PARSE_OK && tok_type != TT_WHITESPACE ) {
1224                        // not whitespace token
1225                        status = PARSE_NO_MATCH;
1226                    }
1227                    break;
1228
1229                default:       // match characters
1230                    {
1231                        status = match_char( scanner, c, case_sensitive );
1232                    }
1233            }
1234        }
1235    }
1236
1237    if( status != PARSE_OK ) {
1238        // on error, restore original scanner pos
1239        scanner->cursor = save_pos;
1240    }
1241
1242    return status;
1243}
1244
1245/************************************************************************
1246* Function: match
1247*
1248* Parameters:
1249*	INOUT scanner_t* scanner ; Scanner Object
1250*	IN const char* fmt;			Pattern format
1251*	...
1252*
1253* Description: matches a variable parameter list and takes necessary
1254*	actions based on the data type specified.
1255*
1256* Returns:
1257*   PARSE_OK
1258*   PARSE_NO_MATCH
1259*   PARSE_INCOMPLETE
1260************************************************************************/
1261static int
1262match( INOUT scanner_t * scanner,
1263       IN const char *fmt,
1264       ... )
1265{
1266    int ret_code;
1267    va_list args;
1268
1269    va_start( args, fmt );
1270    ret_code = vfmatch( scanner, fmt, args );
1271    va_end( args );
1272
1273    return ret_code;
1274}
1275
1276/************************************************************************
1277* Function: matchstr
1278*
1279* Parameters:
1280*	IN char *str ;		 String to be matched
1281*	IN size_t slen ;     Length of the string
1282*	IN const char* fmt ; Pattern format
1283*	...
1284*
1285* Description: Matches a variable parameter list with a string
1286*	and takes actions based on the data type specified.
1287*
1288* Returns:
1289*   PARSE_OK
1290*   PARSE_NO_MATCH -- failure to match pattern 'fmt'
1291*   PARSE_FAILURE	-- 'str' is bad input
1292************************************************************************/
1293int
1294matchstr( IN char *str,
1295          IN size_t slen,
1296          IN const char *fmt,
1297          ... )
1298{
1299    int ret_code;
1300    char save_char;
1301    scanner_t scanner;
1302    membuffer buf;
1303    va_list arg_list;
1304
1305    // null terminate str
1306    save_char = str[slen];
1307    str[slen] = '\0';
1308
1309    membuffer_init( &buf );
1310
1311    // under no circumstances should this buffer be modifed because its memory
1312    //  might have not come from malloc()
1313    membuffer_attach( &buf, str, slen );
1314
1315    scanner_init( &scanner, &buf );
1316    scanner.entire_msg_loaded = TRUE;
1317
1318    va_start( arg_list, fmt );
1319    ret_code = vfmatch( &scanner, fmt, arg_list );
1320    va_end( arg_list );
1321
1322    // restore str
1323    str[slen] = save_char;
1324
1325    // don't destroy buf
1326
1327    return ret_code;
1328}
1329
1330/************************************************************************
1331* Function: parser_init
1332*
1333* Parameters:
1334*	OUT http_parser_t* parser ; HTTP Parser object
1335*
1336* Description: Initializes the parser object.
1337*
1338* Returns:
1339*	void
1340************************************************************************/
1341static XINLINE void
1342parser_init( OUT http_parser_t * parser )
1343{
1344    memset( parser, 0, sizeof( http_parser_t ) );
1345
1346    parser->http_error_code = HTTP_BAD_REQUEST; // err msg by default
1347    parser->ent_position = ENTREAD_DETERMINE_READ_METHOD;
1348    parser->valid_ssdp_notify_hack = FALSE;
1349
1350    httpmsg_init( &parser->msg );
1351    scanner_init( &parser->scanner, &parser->msg.msg );
1352}
1353
1354/************************************************************************
1355* Function: parser_parse_requestline
1356*
1357* Parameters:
1358*	INOUT http_parser_t* parser ; HTTP Parser  object
1359*
1360* Description: Get HTTP Method, URL location and version information.
1361*
1362* Returns:
1363*	PARSE_OK
1364*	PARSE_SUCCESS
1365*	PARSE_FAILURE
1366************************************************************************/
1367static parse_status_t
1368parser_parse_requestline( INOUT http_parser_t * parser )
1369{
1370    parse_status_t status;
1371    http_message_t *hmsg = &parser->msg;
1372    memptr method_str;
1373    memptr version_str;
1374    int index;
1375    char save_char;
1376    int num_scanned;
1377    memptr url_str;
1378
1379    assert( parser->position == POS_REQUEST_LINE );
1380
1381    status = skip_blank_lines( &parser->scanner );
1382    if( status != PARSE_OK ) {
1383        return status;
1384    }
1385    //simple get http 0.9 as described in http 1.0 spec
1386
1387    status =
1388        match( &parser->scanner, "%s\t%S%w%c", &method_str, &url_str );
1389
1390    if( status == PARSE_OK ) {
1391
1392        index =
1393            map_str_to_int( method_str.buf, method_str.length,
1394                            Http_Method_Table, NUM_HTTP_METHODS, TRUE );
1395
1396        if( index < 0 ) {
1397            // error; method not found
1398            parser->http_error_code = HTTP_NOT_IMPLEMENTED;
1399            return PARSE_FAILURE;
1400        }
1401
1402        if( Http_Method_Table[index].id != HTTPMETHOD_GET ) {
1403            parser->http_error_code = HTTP_BAD_REQUEST;
1404            return PARSE_FAILURE;
1405        }
1406
1407        hmsg->method = HTTPMETHOD_SIMPLEGET;
1408
1409        // store url
1410        hmsg->urlbuf = str_alloc( url_str.buf, url_str.length );
1411        if( hmsg->urlbuf == NULL ) {
1412            // out of mem
1413            parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1414            return PARSE_FAILURE;
1415        }
1416        if( parse_uri( hmsg->urlbuf, url_str.length, &hmsg->uri ) !=
1417            HTTP_SUCCESS ) {
1418            return PARSE_FAILURE;
1419        }
1420
1421        parser->position = POS_COMPLETE;    // move to headers
1422
1423        return PARSE_SUCCESS;
1424    }
1425
1426    status = match( &parser->scanner,
1427                    "%s\t%S\t%ihttp%w/%w%L%c", &method_str, &url_str,
1428                    &version_str );
1429    if( status != PARSE_OK ) {
1430        return status;
1431    }
1432    // store url
1433    hmsg->urlbuf = str_alloc( url_str.buf, url_str.length );
1434    if( hmsg->urlbuf == NULL ) {
1435        // out of mem
1436        parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1437        return PARSE_FAILURE;
1438    }
1439    if( parse_uri( hmsg->urlbuf, url_str.length, &hmsg->uri ) !=
1440        HTTP_SUCCESS ) {
1441        return PARSE_FAILURE;
1442    }
1443    // scan version
1444    save_char = version_str.buf[version_str.length];
1445    version_str.buf[version_str.length] = '\0'; // null-terminate
1446    num_scanned = sscanf( version_str.buf, "%d . %d",
1447                          &hmsg->major_version, &hmsg->minor_version );
1448    version_str.buf[version_str.length] = save_char;    // restore
1449    if( num_scanned != 2 ||
1450        hmsg->major_version < 0 || hmsg->minor_version < 0 ) {
1451        // error; bad http version
1452        return PARSE_FAILURE;
1453    }
1454
1455    index =
1456        map_str_to_int( method_str.buf, method_str.length,
1457                        Http_Method_Table, NUM_HTTP_METHODS, TRUE );
1458    if( index < 0 ) {
1459        // error; method not found
1460        parser->http_error_code = HTTP_NOT_IMPLEMENTED;
1461        return PARSE_FAILURE;
1462    }
1463
1464    hmsg->method = Http_Method_Table[index].id;
1465    parser->position = POS_HEADERS; // move to headers
1466
1467    return PARSE_OK;
1468}
1469
1470/************************************************************************
1471* Function: parser_parse_responseline
1472*
1473* Parameters:
1474*	INOUT http_parser_t* parser	; HTTP Parser object
1475*
1476* Description: Get HTTP Method, URL location and version information.
1477*
1478* Returns:
1479*	PARSE_OK
1480*	PARSE_SUCCESS
1481*	PARSE_FAILURE
1482************************************************************************/
1483parse_status_t
1484parser_parse_responseline( INOUT http_parser_t * parser )
1485{
1486    parse_status_t status;
1487    http_message_t *hmsg = &parser->msg;
1488    memptr line;
1489    char save_char;
1490    int num_scanned;
1491    int i;
1492    char *p;
1493
1494    assert( parser->position == POS_RESPONSE_LINE );
1495
1496    status = skip_blank_lines( &parser->scanner );
1497    if( status != PARSE_OK ) {
1498        return status;
1499    }
1500    // response line
1501    //status = match( &parser->scanner, "%ihttp%w/%w%d\t.\t%d\t%d\t%L%c",
1502    //  &hmsg->major_version, &hmsg->minor_version,
1503    //  &hmsg->status_code, &hmsg->status_msg );
1504
1505    status = match( &parser->scanner, "%ihttp%w/%w%L%c", &line );
1506    if( status != PARSE_OK ) {
1507        return status;
1508    }
1509
1510    save_char = line.buf[line.length];
1511    line.buf[line.length] = '\0';   // null-terminate
1512
1513    // scan http version and ret code
1514    num_scanned = sscanf( line.buf, "%d . %d %d",
1515                          &hmsg->major_version, &hmsg->minor_version,
1516                          &hmsg->status_code );
1517
1518    line.buf[line.length] = save_char;  // restore
1519
1520    if( num_scanned != 3 ||
1521        hmsg->major_version < 0 ||
1522        hmsg->minor_version < 0 || hmsg->status_code < 0 ) {
1523        // bad response line
1524        return PARSE_FAILURE;
1525    }
1526    //
1527    // point to status msg
1528    //
1529
1530    p = line.buf;
1531
1532    // skip 3 ints
1533    for( i = 0; i < 3; i++ ) {
1534        // go to start of num
1535        while( !isdigit( *p ) ) {
1536            p++;
1537        }
1538
1539        // skip int
1540        while( isdigit( *p ) ) {
1541            p++;
1542        }
1543    }
1544
1545    // whitespace must exist after status code
1546    if( *p != ' ' && *p != '\t' ) {
1547        return PARSE_FAILURE;
1548    }
1549    // skip whitespace
1550    while( *p == ' ' || *p == '\t' ) {
1551        p++;
1552    }
1553
1554    // now, p is at start of status msg
1555    if( membuffer_assign( &hmsg->status_msg, p,
1556                          line.length - ( p - line.buf ) ) != 0 ) {
1557        // out of mem
1558        parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1559        return PARSE_FAILURE;
1560    }
1561
1562    parser->position = POS_HEADERS; // move to headers
1563
1564    return PARSE_OK;
1565}
1566
1567/************************************************************************
1568* Function: parser_parse_headers
1569*
1570* Parameters:
1571*	INOUT http_parser_t* parser	; HTTP Parser object
1572*
1573* Description: Get HTTP Method, URL location and version information.
1574*
1575* Returns:
1576*	PARSE_OK
1577*	PARSE_SUCCESS
1578*	PARSE_FAILURE
1579************************************************************************/
1580parse_status_t
1581parser_parse_headers( INOUT http_parser_t * parser )
1582{
1583    parse_status_t status;
1584    memptr token_1;
1585    memptr hdr_value;
1586    token_type_t tok_type;
1587    scanner_t *scanner = &parser->scanner;
1588    size_t save_pos;
1589    http_header_t *header;
1590    int header_id;
1591    int ret = 0;
1592    int index_1;
1593    http_header_t *orig_header;
1594    char save_char;
1595    int ret2;
1596
1597    assert( parser->position == POS_HEADERS ||
1598            parser->ent_position == ENTREAD_CHUNKY_HEADERS );
1599
1600    while( TRUE ) {
1601        save_pos = scanner->cursor;
1602
1603        //
1604        // check end of headers
1605        //
1606        status = scanner_get_token( scanner, &token_1, &tok_type );
1607        if( status != PARSE_OK ) {
1608            return status;
1609        }
1610
1611        if( tok_type == TT_CRLF ) {
1612
1613            // end of headers
1614            if( ( parser->msg.is_request )
1615                && ( parser->msg.method == HTTPMETHOD_POST ) ) {
1616                parser->position = POS_COMPLETE;    //post entity parsing
1617                //is handled separately
1618                return PARSE_SUCCESS;
1619            }
1620
1621            parser->position = POS_ENTITY;  // read entity next
1622            return PARSE_OK;
1623        }
1624        //
1625        // not end; read header
1626        //
1627        if( tok_type != TT_IDENTIFIER ) {
1628            return PARSE_FAILURE;   // didn't see header name
1629        }
1630
1631        status = match( scanner, " : %R%c", &hdr_value );
1632        if( status != PARSE_OK ) {
1633            // pushback tokens; useful only on INCOMPLETE error
1634            scanner->cursor = save_pos;
1635            return status;
1636        }
1637        //
1638        // add header
1639        //
1640
1641        // find header
1642        index_1 = map_str_to_int( token_1.buf, token_1.length, Http_Header_Names,
1643                                NUM_HTTP_HEADER_NAMES, FALSE );
1644        if( index_1 != -1 ) {
1645
1646            //Check if it is a soap header
1647            if( Http_Header_Names[index_1].id == HDR_SOAPACTION ) {
1648                parser->msg.method = SOAPMETHOD_POST;
1649            }
1650
1651            header_id = Http_Header_Names[index_1].id;
1652            orig_header =
1653                httpmsg_find_hdr( &parser->msg, header_id, NULL );
1654        } else {
1655            header_id = HDR_UNKNOWN;
1656
1657            save_char = token_1.buf[token_1.length];
1658            token_1.buf[token_1.length] = '\0';
1659
1660            orig_header = httpmsg_find_hdr_str( &parser->msg, token_1.buf );
1661
1662            token_1.buf[token_1.length] = save_char;    // restore
1663        }
1664
1665        if( orig_header == NULL ) {
1666            //
1667            // add new header
1668            //
1669
1670            header = ( http_header_t * ) malloc( sizeof( http_header_t ) );
1671            if( header == NULL ) {
1672                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1673                return PARSE_FAILURE;
1674            }
1675            membuffer_init( &header->name_buf );
1676            membuffer_init( &header->value );
1677
1678            // value can be 0 length
1679            if( hdr_value.length == 0 ) {
1680                hdr_value.buf = "\0";
1681                hdr_value.length = 1;
1682            }
1683            // save in header in buffers
1684            if( membuffer_assign
1685                ( &header->name_buf, token_1.buf, token_1.length ) != 0
1686                || membuffer_assign( &header->value, hdr_value.buf,
1687                                     hdr_value.length ) != 0 ) {
1688                // not enuf mem
1689                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1690                return PARSE_FAILURE;
1691            }
1692
1693            header->name.buf = header->name_buf.buf;
1694            header->name.length = header->name_buf.length;
1695            header->name_id = header_id;
1696
1697            ListAddTail( &parser->msg.headers, header );
1698
1699            //NNS:          ret = dlist_append( &parser->msg.headers, header );
1700            if( ret == UPNP_E_OUTOF_MEMORY ) {
1701                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1702                return PARSE_FAILURE;
1703            }
1704        } else if( hdr_value.length > 0 ) {
1705            //
1706            // append value to existing header
1707            //
1708
1709            // append space
1710            ret = membuffer_append_str( &orig_header->value, ", " );
1711
1712            // append continuation of header value
1713            ret2 = membuffer_append( &orig_header->value,
1714                                     hdr_value.buf, hdr_value.length );
1715
1716            if( ret == UPNP_E_OUTOF_MEMORY || ret2 == UPNP_E_OUTOF_MEMORY ) {
1717                // not enuf mem
1718                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1719                return PARSE_FAILURE;
1720            }
1721        }
1722    }                           // end while
1723
1724}
1725
1726////////////////////////////////////////////////////////////////////////
1727#ifdef HIGHLY_UNLIKELY
1728// **************
1729static parse_status_t
1730parser_parse_headers_old( INOUT http_parser_t * parser )
1731{
1732    parse_status_t status;
1733    memptr token_1;
1734    memptr hdr_value;
1735    token_type_t tok_type;
1736    scanner_t *scanner = &parser->scanner;
1737    size_t save_pos;
1738    http_header_t *header;
1739    int header_id;
1740    int ret = 0;
1741    int index_1;
1742    http_header_t *orig_header;
1743    char save_char;
1744    int ret2,
1745      ret3;
1746
1747    assert( parser->position == POS_HEADERS ||
1748            parser->ent_position == ENTREAD_CHUNKY_HEADERS );
1749
1750    while( TRUE ) {
1751        save_pos = scanner->cursor;
1752
1753        //
1754        // check end of headers
1755        //
1756        status = scanner_get_token( scanner, &token_1, &tok_type );
1757        if( status != PARSE_OK ) {
1758            return status;
1759        }
1760
1761        if( tok_type == TT_CRLF ) {
1762            // end of headers
1763            parser->position = POS_ENTITY;  // read entity next
1764            return PARSE_OK;
1765        }
1766        //
1767        // not end; read header
1768        //
1769        if( tok_type != TT_IDENTIFIER ) {
1770            return PARSE_FAILURE;   // didn't see header name
1771        }
1772
1773        status = match( scanner, " : %R%c", &hdr_value );
1774        if( status != PARSE_OK ) {
1775            // pushback tokens; useful only on INCOMPLETE error
1776            scanner->cursor = save_pos;
1777            return status;
1778        }
1779
1780        //
1781        // add header
1782        //
1783
1784        // find header
1785        index_1 = map_str_to_int( token_1.buf, token_1.length, Http_Header_Names,
1786                                NUM_HTTP_HEADER_NAMES, FALSE );
1787        if( index_1 != -1 ) {
1788            header_id = Http_Header_Names[index_1].id;
1789
1790            orig_header =
1791                httpmsg_find_hdr( &parser->msg, header_id, NULL );
1792        } else {
1793            header_id = HDR_UNKNOWN;
1794
1795            save_char = token_1.buf[token_1.length];
1796            token_1.buf[token_1.length] = '\0';
1797
1798            orig_header = httpmsg_find_hdr_str( &parser->msg, token_1.buf );
1799
1800            token_1.buf[token_1.length] = save_char;    // restore
1801        }
1802
1803        if( orig_header == NULL ) {
1804            //
1805            // add new header
1806            //
1807
1808            header = ( http_header_t * ) malloc( sizeof( http_header_t ) );
1809            if( header == NULL ) {
1810                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1811                return PARSE_FAILURE;
1812            }
1813            membuffer_init( &header->multi_hdr_buf );
1814
1815            header->name = token_1;
1816            header->value = hdr_value;
1817            header->name_id = header_id;
1818
1819            ret = dlist_append( &parser->msg.headers, header );
1820            if( ret == UPNP_E_OUTOF_MEMORY ) {
1821                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1822                return PARSE_FAILURE;
1823            }
1824        } else if( hdr_value.length > 0 ) {
1825            //
1826            // append value to existing header
1827            //
1828
1829            if( orig_header->multi_hdr_buf.buf == NULL ) {
1830                // store in buffer
1831                ret = membuffer_append( &orig_header->multi_hdr_buf,
1832                                        orig_header->value.buf,
1833                                        orig_header->value.length );
1834            }
1835            // append space
1836            ret2 =
1837                membuffer_append( &orig_header->multi_hdr_buf, ", ", 2 );
1838
1839            // append continuation of header value
1840            ret3 = membuffer_append( &orig_header->multi_hdr_buf,
1841                                     hdr_value.buf, hdr_value.length );
1842
1843            if( ret == UPNP_E_OUTOF_MEMORY ||
1844                ret2 == UPNP_E_OUTOF_MEMORY ||
1845                ret3 == UPNP_E_OUTOF_MEMORY ) {
1846                // not enuf mem
1847                parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
1848                return PARSE_FAILURE;
1849            }
1850            // header value points to allocated buf
1851            orig_header->value.buf = orig_header->multi_hdr_buf.buf;
1852            orig_header->value.length = orig_header->multi_hdr_buf.length;
1853        }
1854    }                           // end while
1855
1856}
1857#endif
1858// ******************************
1859
1860/************************************************************************
1861* Function: parser_parse_entity_using_clen
1862*
1863* Parameters:
1864*	INOUT http_parser_t* parser ; HTTP Parser object
1865*
1866* Description: reads entity using content-length
1867*
1868* Returns:
1869*	 PARSE_INCOMPLETE
1870*	 PARSE_FAILURE -- entity length > content-length value
1871*	 PARSE_SUCCESS
1872************************************************************************/
1873static XINLINE parse_status_t
1874parser_parse_entity_using_clen( INOUT http_parser_t * parser )
1875{
1876    //int entity_length;
1877
1878    assert( parser->ent_position == ENTREAD_USING_CLEN );
1879
1880    // determine entity (i.e. body) length so far
1881    //entity_length = parser->msg.msg.length - parser->entity_start_position;
1882    parser->msg.entity.length =
1883        parser->msg.msg.length - parser->entity_start_position;
1884
1885    if( parser->msg.entity.length < parser->content_length ) {
1886        // more data to be read
1887        return PARSE_INCOMPLETE;
1888    } else {
1889        if( parser->msg.entity.length > parser->content_length ) {
1890            // silently discard extra data
1891            parser->msg.msg.buf[parser->entity_start_position +
1892                                parser->content_length] = '\0';
1893        }
1894        // save entity length
1895        parser->msg.entity.length = parser->content_length;
1896
1897        // save entity start ptr; (the very last thing to do)
1898        parser->msg.entity.buf = parser->msg.msg.buf +
1899            parser->entity_start_position;
1900
1901        // done reading entity
1902        parser->position = POS_COMPLETE;
1903        return PARSE_SUCCESS;
1904    }
1905}
1906
1907/************************************************************************
1908* Function: parser_parse_chunky_body
1909*
1910* Parameters:
1911*	INOUT http_parser_t* parser	; HTTP Parser object
1912*
1913* Description: Read data in the chunks
1914*
1915* Returns:
1916*	 PARSE_INCOMPLETE
1917*	 PARSE_FAILURE -- entity length > content-length value
1918*	 PARSE_SUCCESS
1919************************************************************************/
1920static XINLINE parse_status_t
1921parser_parse_chunky_body( INOUT http_parser_t * parser )
1922{
1923    parse_status_t status;
1924    size_t save_pos;
1925
1926    // if 'chunk_size' of bytes have been read; read next chunk
1927    if( ( int )( parser->msg.msg.length - parser->scanner.cursor ) >=
1928        parser->chunk_size ) {
1929        // move to next chunk
1930        parser->scanner.cursor += parser->chunk_size;
1931        save_pos = parser->scanner.cursor;
1932
1933        //discard CRLF
1934        status = match( &parser->scanner, "%c" );
1935        if( status != PARSE_OK ) {
1936            parser->scanner.cursor -= parser->chunk_size;   //move back
1937            //parser->scanner.cursor = save_pos;
1938            return status;
1939        }
1940
1941        membuffer_delete( &parser->msg.msg, save_pos,
1942                          ( parser->scanner.cursor - save_pos ) );
1943        parser->scanner.cursor = save_pos;
1944        parser->msg.entity.length += parser->chunk_size;    //update temp
1945        parser->ent_position = ENTREAD_USING_CHUNKED;
1946        return PARSE_CONTINUE_1;
1947    } else {
1948        return PARSE_INCOMPLETE;    // need more data for chunk
1949    }
1950}
1951
1952/************************************************************************
1953* Function: parser_parse_chunky_headers
1954*
1955* Parameters:
1956*	INOUT http_parser_t* parser	; HTTP Parser object
1957*
1958* Description: Read headers at the end of the chunked entity
1959*
1960* Returns:
1961*	 PARSE_INCOMPLETE
1962*	 PARSE_FAILURE -- entity length > content-length value
1963*	 PARSE_SUCCESS
1964************************************************************************/
1965static XINLINE parse_status_t
1966parser_parse_chunky_headers( INOUT http_parser_t * parser )
1967{
1968    parse_status_t status;
1969    size_t save_pos;
1970
1971    save_pos = parser->scanner.cursor;
1972    status = parser_parse_headers( parser );
1973    if( status == PARSE_OK ) {
1974        // finally, done with the whole msg
1975        parser->position = POS_COMPLETE;
1976
1977        // save entity start ptr as the very last thing to do
1978        parser->msg.entity.buf = parser->msg.msg.buf +
1979            parser->entity_start_position;
1980
1981        membuffer_delete( &parser->msg.msg, save_pos,
1982                          ( parser->scanner.cursor - save_pos ) );
1983        parser->scanner.cursor = save_pos;
1984
1985        return PARSE_SUCCESS;
1986    } else {
1987        return status;
1988    }
1989}
1990
1991/************************************************************************
1992* Function: parser_parse_chunky_entity
1993*
1994* Parameters:
1995*	INOUT http_parser_t* parser	- HTTP Parser Object
1996*
1997* Description: Read headers at the end of the chunked entity
1998*
1999* Returns:
2000*	 PARSE_INCOMPLETE
2001*	 PARSE_FAILURE -- entity length > content-length value
2002*	 PARSE_SUCCESS
2003*	 PARSE_CONTINUE_1
2004************************************************************************/
2005static XINLINE parse_status_t
2006parser_parse_chunky_entity( INOUT http_parser_t * parser )
2007{
2008    scanner_t *scanner = &parser->scanner;
2009    parse_status_t status;
2010    size_t save_pos;
2011    memptr dummy;
2012
2013    assert( parser->ent_position == ENTREAD_USING_CHUNKED );
2014
2015    save_pos = scanner->cursor;
2016
2017    // get size of chunk, discard extension, discard CRLF
2018    status = match( scanner, "%x%L%c", &parser->chunk_size, &dummy );
2019    if( status != PARSE_OK ) {
2020        scanner->cursor = save_pos;
2021        DBGONLY( UpnpPrintf
2022                 ( UPNP_INFO, HTTP, __FILE__, __LINE__,
2023                   "CHUNK COULD NOT BE PARSED\n" ); )
2024            return status;
2025    }
2026    // remove chunk info just matched; just retain data
2027    membuffer_delete( &parser->msg.msg, save_pos,
2028                      ( scanner->cursor - save_pos ) );
2029    scanner->cursor = save_pos; // adjust scanner too
2030
2031    if( parser->chunk_size == 0 ) {
2032        // done reading entity; determine length of entity
2033        parser->msg.entity.length = parser->scanner.cursor -
2034            parser->entity_start_position;
2035
2036        // read entity headers
2037        parser->ent_position = ENTREAD_CHUNKY_HEADERS;
2038    } else {
2039        // read chunk body
2040        parser->ent_position = ENTREAD_CHUNKY_BODY;
2041    }
2042
2043    return PARSE_CONTINUE_1;    // continue to reading body
2044}
2045
2046/************************************************************************
2047* Function: parser_parse_entity_until_close
2048*
2049* Parameters:
2050*	INOUT http_parser_t* parser	; HTTP Parser object
2051*
2052* Description: Read headers at the end of the chunked entity
2053*
2054* Returns:
2055*	 PARSE_INCOMPLETE_ENTITY
2056************************************************************************/
2057static XINLINE parse_status_t
2058parser_parse_entity_until_close( INOUT http_parser_t * parser )
2059{
2060    size_t cursor;
2061
2062    assert( parser->ent_position == ENTREAD_UNTIL_CLOSE );
2063
2064    // eat any and all data
2065    cursor = parser->msg.msg.length;
2066
2067    // update entity length
2068    parser->msg.entity.length = cursor - parser->entity_start_position;
2069
2070    // update pointer
2071    parser->msg.entity.buf =
2072        parser->msg.msg.buf + parser->entity_start_position;
2073
2074    parser->scanner.cursor = cursor;
2075
2076    return PARSE_INCOMPLETE_ENTITY; // add anything
2077}
2078
2079/************************************************************************
2080* Function: parser_get_entity_read_method
2081*
2082* Parameters:
2083*	INOUT http_parser_t* parser	; HTTP Parser object
2084*
2085* Description: Determines method to read entity
2086*
2087* Returns:
2088*	 PARSE_OK
2089* 	 PARSE_FAILURE
2090*	 PARSE_COMPLETE	-- no more reading to do
2091************************************************************************/
2092XINLINE parse_status_t
2093parser_get_entity_read_method( INOUT http_parser_t * parser )
2094{
2095    http_message_t *hmsg = &parser->msg;
2096    int response_code;
2097    memptr hdr_value;
2098
2099    assert( parser->ent_position == ENTREAD_DETERMINE_READ_METHOD );
2100
2101    // entity points to start of msg body
2102    parser->msg.entity.buf = scanner_get_str( &parser->scanner );
2103    parser->msg.entity.length = 0;
2104
2105    // remember start of body
2106    parser->entity_start_position = parser->scanner.cursor;
2107
2108    // std http rules for determining content length
2109
2110    // * no body for 1xx, 204, 304 and HEAD, GET,
2111    //      SUBSCRIBE, UNSUBSCRIBE
2112    if( hmsg->is_request ) {
2113        switch ( hmsg->method ) {
2114            case HTTPMETHOD_HEAD:
2115            case HTTPMETHOD_GET:
2116                //case HTTPMETHOD_POST:
2117            case HTTPMETHOD_SUBSCRIBE:
2118            case HTTPMETHOD_UNSUBSCRIBE:
2119            case HTTPMETHOD_MSEARCH:
2120                // no body; mark as done
2121                parser->position = POS_COMPLETE;
2122                return PARSE_SUCCESS;
2123                break;
2124
2125            default:
2126                ;               // do nothing
2127        }
2128    } else                      // response
2129    {
2130        response_code = hmsg->status_code;
2131
2132        if( response_code == 204 ||
2133            response_code == 304 ||
2134            ( response_code >= 100 && response_code <= 199 ) ||
2135            hmsg->request_method == HTTPMETHOD_HEAD ||
2136            hmsg->request_method == HTTPMETHOD_MSEARCH ||
2137            hmsg->request_method == HTTPMETHOD_SUBSCRIBE ||
2138            hmsg->request_method == HTTPMETHOD_UNSUBSCRIBE ||
2139            hmsg->request_method == HTTPMETHOD_NOTIFY ) {
2140            parser->position = POS_COMPLETE;
2141            return PARSE_SUCCESS;
2142        }
2143    }
2144
2145    // * transfer-encoding -- used to indicate chunked data
2146    if( httpmsg_find_hdr( hmsg, HDR_TRANSFER_ENCODING, &hdr_value ) ) {
2147        if( raw_find_str( &hdr_value, "chunked" ) >= 0 ) {
2148            // read method to use chunked transfer encoding
2149            parser->ent_position = ENTREAD_USING_CHUNKED;
2150            DBGONLY( UpnpPrintf
2151                     ( UPNP_INFO, HTTP, __FILE__, __LINE__,
2152                       "Found Chunked Encoding ....\n" ); )
2153
2154                return PARSE_CONTINUE_1;
2155        }
2156    }
2157    // * use content length
2158    if( httpmsg_find_hdr( hmsg, HDR_CONTENT_LENGTH, &hdr_value ) ) {
2159        parser->content_length = raw_to_int( &hdr_value, 10 );
2160        if( parser->content_length < 0 ) {
2161            // bad content-length
2162            return PARSE_FAILURE;
2163        }
2164        parser->ent_position = ENTREAD_USING_CLEN;
2165        return PARSE_CONTINUE_1;
2166    }
2167    // * multi-part/byteranges not supported (yet)
2168
2169    // * read until connection is closed
2170    if( hmsg->is_request ) {
2171        // set hack flag for NOTIFY methods; if set to true this is
2172        //  a valid SSDP notify msg
2173        if( hmsg->method == HTTPMETHOD_NOTIFY ) {
2174            parser->valid_ssdp_notify_hack = TRUE;
2175        }
2176
2177        parser->http_error_code = HTTP_LENGTH_REQUIRED;
2178        return PARSE_FAILURE;
2179    }
2180
2181    parser->ent_position = ENTREAD_UNTIL_CLOSE;
2182    return PARSE_CONTINUE_1;
2183}
2184
2185/************************************************************************
2186* Function: parser_parse_entity
2187*
2188* Parameters:
2189*	INOUT http_parser_t* parser	; HTTP Parser object
2190*
2191* Description: Determines method to read entity
2192*
2193* Returns:
2194*	 PARSE_OK
2195* 	 PARSE_FAILURE
2196*	 PARSE_COMPLETE	-- no more reading to do
2197************************************************************************/
2198XINLINE parse_status_t
2199parser_parse_entity( INOUT http_parser_t * parser )
2200{
2201    parse_status_t status = PARSE_OK;
2202
2203    assert( parser->position == POS_ENTITY );
2204
2205    do {
2206        switch ( parser->ent_position ) {
2207            case ENTREAD_USING_CLEN:
2208                status = parser_parse_entity_using_clen( parser );
2209                break;
2210
2211            case ENTREAD_USING_CHUNKED:
2212                status = parser_parse_chunky_entity( parser );
2213                break;
2214
2215            case ENTREAD_CHUNKY_BODY:
2216                status = parser_parse_chunky_body( parser );
2217                break;
2218
2219            case ENTREAD_CHUNKY_HEADERS:
2220                status = parser_parse_chunky_headers( parser );
2221                break;
2222
2223            case ENTREAD_UNTIL_CLOSE:
2224                status = parser_parse_entity_until_close( parser );
2225                break;
2226
2227            case ENTREAD_DETERMINE_READ_METHOD:
2228                status = parser_get_entity_read_method( parser );
2229                break;
2230
2231            default:
2232                assert( 0 );
2233        }
2234
2235    } while( status == PARSE_CONTINUE_1 );
2236
2237    return status;
2238}
2239
2240/************************************************************************
2241* Function: parser_request_init
2242*
2243* Parameters:
2244*	OUT http_parser_t* parser ; HTTP Parser object
2245*
2246* Description: Initializes parser object for a request
2247*
2248* Returns:
2249*	 void
2250************************************************************************/
2251void
2252parser_request_init( OUT http_parser_t * parser )
2253{
2254    parser_init( parser );
2255    parser->msg.is_request = TRUE;
2256    parser->position = POS_REQUEST_LINE;
2257}
2258
2259/************************************************************************
2260* Function: parser_response_init
2261*
2262* Parameters:
2263*	OUT http_parser_t* parser	;	  HTTP Parser object
2264*	IN http_method_t request_method	; Request method
2265*
2266* Description: Initializes parser object for a response
2267*
2268* Returns:
2269*	 void
2270************************************************************************/
2271void
2272parser_response_init( OUT http_parser_t * parser,
2273                      IN http_method_t request_method )
2274{
2275    parser_init( parser );
2276    parser->msg.is_request = FALSE;
2277    parser->msg.request_method = request_method;
2278    parser->position = POS_RESPONSE_LINE;
2279}
2280
2281/************************************************************************
2282* Function: parser_parse
2283*
2284* Parameters:
2285*	INOUT http_parser_t* parser ; HTTP Parser object
2286*
2287* Description: The parser function. Depending on the position of the
2288*	parser object the actual parsing function is invoked
2289*
2290* Returns:
2291*	 void
2292************************************************************************/
2293parse_status_t
2294parser_parse( INOUT http_parser_t * parser )
2295{
2296    parse_status_t status;
2297
2298    //takes an http_parser_t with memory already allocated
2299    //in the message
2300    assert( parser != NULL );
2301
2302    do {
2303        switch ( parser->position ) {
2304            case POS_ENTITY:
2305                status = parser_parse_entity( parser );
2306
2307                break;
2308
2309            case POS_HEADERS:
2310                status = parser_parse_headers( parser );
2311
2312                break;
2313
2314            case POS_REQUEST_LINE:
2315                status = parser_parse_requestline( parser );
2316
2317                break;
2318
2319            case POS_RESPONSE_LINE:
2320                status = parser_parse_responseline( parser );
2321
2322                break;
2323
2324            default:
2325                {
2326                    status = PARSE_FAILURE;
2327                    assert( 0 );
2328                }
2329        }
2330
2331    } while( status == PARSE_OK );
2332
2333    return status;
2334
2335}
2336
2337/************************************************************************
2338* Function: parser_append
2339*
2340* Parameters:
2341*	INOUT http_parser_t* parser ;	HTTP Parser Object
2342*	IN const char* buf	;			buffer to be appended to the parser
2343*									buffer
2344*	IN size_t buf_length ;			Size of the buffer
2345*
2346* Description: The parser function. Depending on the position of the
2347*	parser object the actual parsing function is invoked
2348*
2349* Returns:
2350*	 void
2351************************************************************************/
2352parse_status_t
2353parser_append( INOUT http_parser_t * parser,
2354               IN const char *buf,
2355               IN size_t buf_length )
2356{
2357    int ret_code;
2358
2359    assert( parser != NULL );
2360    assert( buf != NULL );
2361
2362    // append data to buffer
2363    ret_code = membuffer_append( &parser->msg.msg, buf, buf_length );
2364    if( ret_code != 0 ) {
2365        // set failure status
2366        parser->http_error_code = HTTP_INTERNAL_SERVER_ERROR;
2367        return PARSE_FAILURE;
2368    }
2369
2370    return parser_parse( parser );
2371}
2372
2373/************************************************************************
2374**********					end of parser					  ***********
2375************************************************************************/
2376
2377/************************************************************************
2378* Function: raw_to_int
2379*
2380* Parameters:
2381*	IN memptr* raw_value ;	Buffer to be converted
2382*	IN int base ;			Base  to use for conversion
2383*
2384* Description: Converts raw character data to long-integer value
2385*
2386* Returns:
2387*	 int
2388************************************************************************/
2389int
2390raw_to_int( IN memptr * raw_value,
2391            IN int base )
2392{
2393    int num;
2394    char *end_ptr;
2395
2396    if( raw_value->length == 0 ) {
2397        return -1;
2398    }
2399
2400    errno = 0;
2401    num = strtol( raw_value->buf, &end_ptr, base );
2402    if( ( num < 0 )
2403        // all and only those chars in token should be used for num
2404        || ( end_ptr != raw_value->buf + raw_value->length )
2405        || ( ( num == LONG_MIN || num == LONG_MAX )
2406             && ( errno == ERANGE ) )
2407         ) {
2408        return -1;
2409    }
2410    return num;
2411
2412}
2413
2414/************************************************************************
2415* Function: raw_find_str
2416*
2417* Parameters:
2418*	IN memptr* raw_value ; Buffer containg the string
2419*	IN const char* str ;	Substring to be found
2420*
2421* Description: Find a substring from raw character string buffer
2422*
2423* Returns:
2424*	 int - index at which the substring is found.
2425************************************************************************/
2426int
2427raw_find_str( IN memptr * raw_value,
2428              IN const char *str )
2429{
2430    char c;
2431    char *ptr;
2432
2433    c = raw_value->buf[raw_value->length];  // save
2434    raw_value->buf[raw_value->length] = 0;  // null-terminate
2435
2436    ptr = strstr( raw_value->buf, str );
2437
2438    raw_value->buf[raw_value->length] = c;  // restore
2439
2440    if( ptr == 0 ) {
2441        return -1;
2442    }
2443
2444    return ptr - raw_value->buf;    // return index
2445}
2446
2447/************************************************************************
2448* Function: method_to_str
2449*
2450* Parameters:
2451* IN http_method_t method ; HTTP method
2452*
2453* Description: A wrapper function that maps a method id to a method
2454*	nameConverts a http_method id stored in the HTTP Method
2455*
2456* Returns:
2457*	 const char* ptr - Ptr to the HTTP Method																							*
2458************************************************************************/
2459const char *
2460method_to_str( IN http_method_t method )
2461{
2462    int index;
2463
2464    index = map_int_to_str( method, Http_Method_Table, NUM_HTTP_METHODS );
2465
2466    assert( index != -1 );
2467
2468    return index == -1 ? NULL : Http_Method_Table[index].name;
2469}
2470
2471/************************************************************************
2472* Function: print_http_headers
2473*
2474* Parameters:
2475*	http_message_t* hmsg ; HTTP Message object
2476*
2477* Description:
2478*
2479* Returns:
2480*	 void
2481************************************************************************/
2482void
2483print_http_headers( http_message_t * hmsg )
2484{
2485
2486    ListNode *node;
2487
2488    //NNS:  dlist_node *node;
2489    http_header_t *header;
2490
2491    // print start line
2492    if( hmsg->is_request ) {
2493        //printf( "method = %d, version = %d.%d, url = %.*s\n",
2494        //  hmsg->method, hmsg->major_version, hmsg->minor_version,
2495        //  hmsg->uri.pathquery.size, hmsg->uri.pathquery.buff);
2496    } else {
2497        //  printf( "resp status = %d, version = %d.%d, status msg = %.*s\n",
2498        //  hmsg->status_code, hmsg->major_version, hmsg->minor_version,
2499        //  (int)hmsg->status_msg.length, hmsg->status_msg.buf);
2500    }
2501
2502    // print headers
2503
2504    node = ListHead( &hmsg->headers );
2505    //NNS: node = dlist_first_node( &hmsg->headers );
2506    while( node != NULL ) {
2507
2508        header = ( http_header_t * ) node->item;
2509        //NNS: header = (http_header_t *)node->data;
2510        //printf( "hdr name: %.*s, value: %.*s\n",
2511        //  (int)header->name.length, header->name.buf,
2512        //  (int)header->value.length, header->value.buf );
2513
2514        node = ListNext( &hmsg->headers, node );
2515
2516        //NNS: node = dlist_next( &hmsg->headers, node );
2517    }
2518}
2519