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 / uri / uri.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 /*!
34  * \file
35  *
36  * \brief Contains functions for uri, url parsing utility.
37  */
38
39
40 #ifdef __FreeBSD__
41         #include <osreldate.h>
42         #if __FreeBSD_version < 601103
43                 #include <lwres/netdb.h>
44         #endif
45 #endif
46
47
48 #include "config.h"
49
50
51 #include "uri.h"
52
53
54 #include "upnpdebug.h"
55
56
57 /*!
58  * \brief Returns a 1 if a char is a RESERVED char as defined in 
59  * http://www.ietf.org/rfc/rfc2396.txt RFC explaining URIs).
60  *
61  * \return 1 if char is a RESERVED char.
62  */
63 static int is_reserved(
64         /*! [in] Char to be matched for RESERVED characters. */
65         char in)
66 {
67         if (strchr(RESERVED, in)) {
68                 return 1;
69         } else {
70                 return 0;
71         }
72 }
73
74
75 /*!
76  * \brief Returns a 1 if a char is a MARK char as defined in
77  * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs).
78  *
79  * \return 1 if char is a MARKED char.
80  */
81 int is_mark(
82         /*! [in] Char to be matched for MARKED characters. */
83         char in)
84 {
85         if (strchr(MARK, in)) {
86                 return 1;
87         } else {
88                 return 0;
89         }
90 }
91
92
93 /*!
94  * \brief Returns a 1 if a char is an UNRESERVED char as defined in
95  * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs).
96  *
97  * \return 1 if char is a UNRESERVED char.
98  */
99 int is_unreserved(
100         /*! [in] Char to be matched for UNRESERVED characters. */
101         char in)
102 {
103         if (isalnum(in) || is_mark(in)) {
104                 return 1;
105         } else {
106                 return 0;
107         }
108 }
109
110
111 /*!
112  * \brief Returns a 1 if a char[3] sequence is ESCAPED as defined in
113  * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs).
114  *
115  * Size of array is NOT checked (MUST be checked by caller).
116  *
117  * \return 1 if char is a ESCAPED char.
118  */
119 int is_escaped(
120         /*! [in] Char sequence to be matched for ESCAPED characters. */
121         const char *in)
122 {
123         if (in[0] == '%' && isxdigit(in[1]) && isxdigit(in[2])) {
124                 return 1;
125         } else {
126                 return 0;
127         }
128 }
129
130
131 int replace_escaped(char *in, int index, size_t *max)
132 {
133         int tempInt = 0;
134         char tempChar = 0;
135         int i = 0;
136         int j = 0;
137
138         if (in[index] == '%' && isxdigit(in[index + 1]) && isxdigit(in[index + 2])) {
139                 /* Note the "%2x", makes sure that we convert a maximum of two
140                  * characters. */
141                 if (sscanf(&in[index + 1], "%2x", &tempInt) != 1) {
142                         return 0;
143                 }
144
145                 tempChar = ( char )tempInt;
146                 for (i = index + 3, j = index; j < *max; i++, j++) {
147                         in[j] = tempChar;
148                         if (i < *max) {
149                                 tempChar = in[i];
150                         } else {
151                                 tempChar = 0;
152                         }
153                 }
154                 *max -= 2;
155                 return 1;
156         } else {
157                 return 0;
158         }
159 }
160
161
162 /*!
163  * \brief Parses a string of uric characters starting at in[0] as defined in
164  * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs).
165  *
166  * \return 
167  */
168 static int parse_uric(
169         /*! [in] String of characters. */
170         const char *in,
171         /*! [in] Maximum limit. */
172         int max,
173         /*! [out] Token object where the string of characters is copied. */
174         token *out)
175 {
176         int i = 0;
177
178         while (i < max &&
179                (is_unreserved(in[i]) ||
180                 is_reserved(in[i])   ||
181                 ((i + 2 < max) && is_escaped(&in[i])))) {
182                 i++;
183         }
184
185         out->size = i;
186         out->buff = in;
187         return i;
188 }
189
190 /************************************************************************
191 *       Function :      copy_sockaddr_in
192 *
193 *       Parameters :
194 *               const struct sockaddr_in *in ;  Source socket address object
195 *               struct sockaddr_in *out ;               Destination socket address object
196 *
197 *       Description : Copies one socket address into another
198 *
199 *       Return : void ;
200 *
201 *       Note :
202 ************************************************************************/
203 void
204 copy_sockaddr_in( const struct sockaddr_in *in,
205                   struct sockaddr_in *out )
206 {
207     memset( out->sin_zero, 0, 8 );
208     out->sin_family = in->sin_family;
209     out->sin_port = in->sin_port;
210     out->sin_addr.s_addr = in->sin_addr.s_addr;
211 }
212
213 /*!
214  * \brief Tokens are generally pointers into other strings. This copies the
215  * offset and size from a token (in) relative to one string (in_base) into
216  * a token (out) relative to another string (out_base).
217  */
218 static void copy_token(
219         /*! [in] Source token. */
220         const token *in,
221         /*! [in] . */
222         const char *in_base,
223         /*! [out] Destination token. */
224         token *out,
225         /*! [in] . */
226         char *out_base)
227 {
228         out->size = in->size;
229         out->buff = out_base + (in->buff - in_base);
230 }
231
232
233 int copy_URL_list(URL_list *in, URL_list *out)
234 {
235     int len = strlen( in->URLs ) + 1;
236     int i = 0;
237
238     out->URLs = NULL;
239     out->parsedURLs = NULL;
240     out->size = 0;
241
242     out->URLs = ( char * )malloc( len );
243     out->parsedURLs =
244         ( uri_type * ) malloc( sizeof( uri_type ) * in->size );
245
246     if( ( out->URLs == NULL ) || ( out->parsedURLs == NULL ) )
247         return UPNP_E_OUTOF_MEMORY;
248
249     memcpy( out->URLs, in->URLs, len );
250
251     for( i = 0; i < in->size; i++ ) {
252         //copy the parsed uri
253         out->parsedURLs[i].type = in->parsedURLs[i].type;
254         copy_token( &in->parsedURLs[i].scheme, in->URLs,
255                     &out->parsedURLs[i].scheme, out->URLs );
256
257         out->parsedURLs[i].path_type = in->parsedURLs[i].path_type;
258         copy_token( &in->parsedURLs[i].pathquery, in->URLs,
259                     &out->parsedURLs[i].pathquery, out->URLs );
260         copy_token( &in->parsedURLs[i].fragment, in->URLs,
261                     &out->parsedURLs[i].fragment, out->URLs );
262         copy_token( &in->parsedURLs[i].hostport.text,
263                     in->URLs, &out->parsedURLs[i].hostport.text,
264                     out->URLs );
265
266         copy_sockaddr_in( &in->parsedURLs[i].hostport.IPv4address,
267                           &out->parsedURLs[i].hostport.IPv4address );
268     }
269     out->size = in->size;
270
271     return HTTP_SUCCESS;
272 }
273
274
275 void free_URL_list(URL_list *list)
276 {
277         if (list->URLs) {
278                 free(list->URLs);
279         }
280         if (list->parsedURLs) {
281                 free(list->parsedURLs);
282         }
283         list->size = 0;
284 }
285
286
287 #ifdef DEBUG
288 void print_uri(uri_type *in)
289 {
290         print_token(&in->scheme);
291         print_token(&in->hostport.text);
292         print_token(&in->pathquery);
293         print_token(&in->fragment);
294 }
295 #endif /* DEBUG */
296
297
298 #ifdef DEBUG
299 void print_token(token * in)
300 {
301     int i = 0;
302     printf( "Token Size : %"PRIzu"\n\'", in->size );
303     for( i = 0; i < in->size; i++ ) {
304         putchar( in->buff[i] );
305     }
306     putchar( '\'' );
307     putchar( '\n' );
308 }
309 #endif /* DEBUG */
310
311
312 int token_string_casecmp(token *in1, char *in2)
313 {
314         int in2_length = strlen(in2);
315
316         if (in1->size != in2_length) {
317                 return 1;
318         } else {
319                 return strncasecmp(in1->buff, in2, in1->size);
320         }
321 }
322
323
324 int token_string_cmp(token * in1, char *in2)
325 {
326         int in2_length = strlen(in2);
327
328         if (in1->size != in2_length) {
329                 return 1;
330         } else {
331                 return strncmp(in1->buff, in2, in1->size);
332         }
333 }
334
335
336 int token_cmp(token *in1, token *in2)
337 {
338         if (in1->size != in2->size) {
339                 return 1;
340         } else {
341                 return memcmp(in1->buff, in2->buff, in1->size);
342         }
343 }
344
345
346 int parse_port(int max, const char *port, unsigned short *out)
347 {
348         const char *finger = port;
349         const char *max_ptr = finger + max;
350         unsigned short temp = 0;
351
352         while((finger < max_ptr) && (isdigit(*finger))) {
353                 temp = temp * 10;
354                 temp += *finger - '0';
355                 finger++;
356         }
357
358         *out = htons(temp);
359         return finger - port;
360 }
361
362
363 int parse_hostport(
364         const char *in,
365         int max,
366         hostport_type *out)
367 {
368 #define BUFFER_SIZE 8192
369
370     int i = 0;
371     int begin_port;
372     int hostport_size = 0;
373     int host_size = 0;
374 #if !defined(WIN32) && !(defined(__OSX__) || defined(__APPLE__))
375     char temp_hostbyname_buff[BUFFER_SIZE];
376     struct hostent h_buf;
377 #endif
378     struct hostent *h = NULL;
379     int errcode = 0;
380     char *temp_host_name = NULL;
381     int last_dot = -1;
382
383     memset( out, 0, sizeof(hostport_type) );
384
385     out->IPv4address.sin_port = htons( 80 );    //default port is 80
386     memset( &out->IPv4address.sin_zero, 0, 8 );
387
388     while( ( i < max ) && ( in[i] != ':' ) && ( in[i] != '/' )
389            && ( ( isalnum( in[i] ) ) || ( in[i] == '.' )
390                 || ( in[i] == '-' ) ) ) {
391         i++;
392         if( in[i] == '.' ) {
393             last_dot = i;
394         }
395     }
396
397     host_size = i;
398
399     if( ( i < max ) && ( in[i] == ':' ) ) {
400         begin_port = i + 1;
401         //convert port
402         if( !( hostport_size = parse_port( max - begin_port,
403                                            &in[begin_port],
404                                            &out->IPv4address.sin_port ) ) )
405         {
406             return UPNP_E_INVALID_URL;
407         }
408         hostport_size += begin_port;
409     } else
410         hostport_size = host_size;
411
412     //convert to temporary null terminated string
413     temp_host_name = ( char * )malloc( host_size + 1 );
414
415     if( temp_host_name == NULL )
416         return UPNP_E_OUTOF_MEMORY;
417
418     memcpy( temp_host_name, in, host_size );
419     temp_host_name[host_size] = '\0';
420
421     //check to see if host name is an ipv4 address
422     if( ( last_dot != -1 ) && ( last_dot + 1 < host_size )
423         && ( isdigit( temp_host_name[last_dot + 1] ) ) ) {
424         //must be ipv4 address
425
426         errcode = inet_pton( AF_INET,
427                              temp_host_name, &out->IPv4address.sin_addr );
428         if( errcode == 1 ) {
429             out->IPv4address.sin_family = AF_INET;
430         } else {
431             out->IPv4address.sin_addr.s_addr = 0;
432             out->IPv4address.sin_family = AF_INET;
433             free( temp_host_name );
434             temp_host_name = NULL;
435             return UPNP_E_INVALID_URL;
436         }
437     } else {
438         int errCode = 0;
439
440         //call gethostbyname_r (reentrant form of gethostbyname)
441         // TODO: Use autoconf to discover this rather than the
442         // platform-specific stuff below
443 #if defined(WIN32) || defined(__CYGWIN__)
444         h = gethostbyname(temp_host_name);
445 #elif defined(SPARC_SOLARIS)
446         errCode = gethostbyname_r(
447                 temp_host_name,
448                 &h,
449                 temp_hostbyname_buff,
450                 BUFFER_SIZE, &errcode );
451 #elif defined(__FreeBSD__) && __FreeBSD_version < 601103
452         h = lwres_gethostbyname_r(
453                 temp_host_name,
454                 &h_buf,
455                 temp_hostbyname_buff,
456                 BUFFER_SIZE, &errcode );
457         if ( h == NULL ) {
458                 errCode = 1;
459         }
460 #elif defined(__OSX__) || defined(__APPLE__)
461         h = gethostbyname(temp_host_name);
462         if ( h == NULL ) {
463                 errCode = 1;
464         }
465 #elif defined(__linux__)
466         errCode = gethostbyname_r(
467                 temp_host_name,
468                 &h_buf,
469                 temp_hostbyname_buff,
470                 BUFFER_SIZE, &h, &errcode );
471 #else
472         {
473             struct addrinfo hints, *res, *res0;
474
475         h = NULL;
476         memset(&hints, 0, sizeof(hints));
477         hints.ai_family = PF_INET;
478         hints.ai_socktype = SOCK_STREAM;
479         errCode = getaddrinfo(temp_host_name, "http", &hints, &res0);
480
481         if (!errCode) {
482             for (res = res0; res; res = res->ai_next) {
483                 if (res->ai_family == PF_INET &&
484                     res->ai_addr->sa_family == AF_INET)
485                 {
486                     h = &h_buf;
487                     h->h_addrtype = res->ai_addr->sa_family;
488                     h->h_length = 4;
489                     h->h_addr = (void *) temp_hostbyname_buff;
490                     *(struct in_addr *)h->h_addr =
491                         ((struct sockaddr_in *)res->ai_addr)->sin_addr;
492                         break;
493                 }
494             }
495             freeaddrinfo(res0);
496         }
497         }
498 #endif 
499         if( errCode == 0 ) {
500             if( h ) {
501                 if( ( h->h_addrtype == AF_INET ) && ( h->h_length == 4 ) ) {
502                     out->IPv4address.sin_addr =
503                         ( *( struct in_addr * )h->h_addr );
504                     out->IPv4address.sin_family = AF_INET;
505
506                 }
507             }
508         } else {
509             out->IPv4address.sin_addr.s_addr = 0;
510             out->IPv4address.sin_family = AF_INET;
511             free( temp_host_name );
512             temp_host_name = NULL;
513             return UPNP_E_INVALID_URL;
514         }
515     }
516
517     if( temp_host_name ) {
518         free( temp_host_name );
519         temp_host_name = NULL;
520     }
521
522     out->text.size = hostport_size;
523     out->text.buff = in;
524     return hostport_size;
525
526 }
527
528 /*!
529  * \brief parses a uri scheme starting at in[0] as defined in 
530  * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs).
531  *
532  * (e.g. "http:" -> scheme= "http").
533  *
534  * \note String MUST include ':' within the max charcters.
535  *
536  * \return 
537  */
538 static int parse_scheme(
539         /*! [in] String of characters representing a scheme. */
540         const char *in,
541         /*! [in] Maximum number of characters. */
542         int max,
543         /*! [out] Output parameter whose buffer is filled in with the scheme. */
544         token *out)
545 {
546     int i = 0;
547
548     out->size = 0;
549     out->buff = NULL;
550
551     if( ( max == 0 ) || ( !isalpha( in[0] ) ) )
552         return FALSE;
553
554     i++;
555     while( ( i < max ) && ( in[i] != ':' ) ) {
556
557         if( !( isalnum( in[i] ) || ( in[i] == '+' ) || ( in[i] == '-' )
558                || ( in[i] == '.' ) ) )
559             return FALSE;
560
561         i++;
562     }
563     if( i < max ) {
564         out->size = i;
565         out->buff = &in[0];
566         return i;
567     }
568
569     return FALSE;
570
571 }
572
573
574 int remove_escaped_chars(INOUT char *in, INOUT size_t *size )
575 {
576     int i = 0;
577
578     for( i = 0; i < *size; i++ ) {
579         replace_escaped( in, i, size );
580     }
581     return UPNP_E_SUCCESS;
582 }
583
584
585 int remove_dots(char *in, size_t size)
586 {
587     char *copyTo = in;
588     char *copyFrom = in;
589     char *max = in + size;
590     char **Segments = NULL;
591     int lastSegment = -1;
592
593     Segments = malloc( sizeof( char * ) * size );
594
595     if( Segments == NULL )
596         return UPNP_E_OUTOF_MEMORY;
597
598     Segments[0] = NULL;
599     UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
600         "REMOVE_DOTS: before: %s\n", in );
601     while( ( copyFrom < max ) && ( *copyFrom != '?' )
602            && ( *copyFrom != '#' ) ) {
603
604         if( ( ( *copyFrom ) == '.' )
605             && ( ( copyFrom == in ) || ( *( copyFrom - 1 ) == '/' ) ) ) {
606             if( ( copyFrom + 1 == max )
607                 || ( *( copyFrom + 1 ) == '/' ) ) {
608
609                 copyFrom += 2;
610                 continue;
611             } else if( ( *( copyFrom + 1 ) == '.' )
612                        && ( ( copyFrom + 2 == max )
613                             || ( *( copyFrom + 2 ) == '/' ) ) ) {
614                 copyFrom += 3;
615
616                 if( lastSegment > 0 ) {
617                     copyTo = Segments[--lastSegment];
618                 } else {
619                     free( Segments );
620                     //TRACE("ERROR RESOLVING URL, ../ at ROOT");
621                     return UPNP_E_INVALID_URL;
622                 }
623                 continue;
624             }
625         }
626
627         if( ( *copyFrom ) == '/' ) {
628
629             lastSegment++;
630             Segments[lastSegment] = copyTo + 1;
631         }
632         ( *copyTo ) = ( *copyFrom );
633         copyTo++;
634         copyFrom++;
635     }
636     if( copyFrom < max ) {
637         while( copyFrom < max ) {
638             ( *copyTo ) = ( *copyFrom );
639             copyTo++;
640             copyFrom++;
641         }
642     }
643     ( *copyTo ) = 0;
644     free( Segments );
645     UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__,
646         "REMOVE_DOTS: after: %s\n", in );
647     return UPNP_E_SUCCESS;
648 }
649
650
651 char *resolve_rel_url(char *base_url, char *rel_url)
652 {
653     uri_type base;
654     uri_type rel;
655     char temp_path = '/';
656
657     int i = 0;
658     char *finger = NULL;
659
660     char *last_slash = NULL;
661
662     char *out = NULL;
663     char *out_finger = NULL;
664
665     if( base_url && rel_url ) {
666         out =
667             ( char * )malloc( strlen( base_url ) + strlen( rel_url ) + 2 );
668         out_finger = out;
669     } else {
670         if( rel_url )
671             return strdup( rel_url );
672         else
673             return NULL;
674     }
675
676     if( out == NULL ) {
677         return NULL;
678     }
679
680     if( ( parse_uri( rel_url, strlen( rel_url ), &rel ) ) == HTTP_SUCCESS ) {
681
682         if( rel.type == ABSOLUTE ) {
683
684             strcpy( out, rel_url );
685         } else {
686
687             if( ( parse_uri( base_url, strlen( base_url ), &base ) ==
688                   HTTP_SUCCESS )
689                 && ( base.type == ABSOLUTE ) ) {
690
691                 if( strlen( rel_url ) == 0 ) {
692                     strcpy( out, base_url );
693                 } else {
694                     memcpy( out, base.scheme.buff, base.scheme.size );
695                     out_finger += base.scheme.size;
696                     ( *out_finger ) = ':';
697                     out_finger++;
698
699                     if( rel.hostport.text.size > 0 ) {
700                         sprintf( out_finger, "%s", rel_url );
701                     } else {
702                         if( base.hostport.text.size > 0 ) {
703                             memcpy( out_finger, "//", 2 );
704                             out_finger += 2;
705                             memcpy( out_finger, base.hostport.text.buff,
706                                     base.hostport.text.size );
707                             out_finger += base.hostport.text.size;
708                         }
709
710                         if( rel.path_type == ABS_PATH ) {
711                             strcpy( out_finger, rel_url );
712
713                         } else {
714
715                             if( base.pathquery.size == 0 ) {
716                                 base.pathquery.size = 1;
717                                 base.pathquery.buff = &temp_path;
718                             }
719
720                             finger = out_finger;
721                             last_slash = finger;
722                             i = 0;
723
724                             while( ( i < base.pathquery.size ) &&
725                                    ( base.pathquery.buff[i] != '?' ) ) {
726                                 ( *finger ) = base.pathquery.buff[i];
727                                 if( base.pathquery.buff[i] == '/' )
728                                     last_slash = finger + 1;
729                                 i++;
730                                 finger++;
731
732                             }
733                             i = 0;
734                             strcpy( last_slash, rel_url );
735                             if( remove_dots( out_finger,
736                                              strlen( out_finger ) ) !=
737                                 UPNP_E_SUCCESS ) {
738                                 free(out);
739                                 /* free(rel_url); */
740                                 return NULL;
741                             }
742                         }
743
744                     }
745                 }
746             } else {
747                 free(out);
748                 /* free(rel_url); */
749                 return NULL;
750             }
751         }
752     } else {
753         free(out);
754         /* free(rel_url); */          
755         return NULL;
756     }
757
758     /* free(rel_url); */
759     return out;
760 }
761
762
763 int parse_uri(const char *in, int max, uri_type *out)
764 {
765     int begin_path = 0;
766     int begin_hostport = 0;
767     int begin_fragment = 0;
768
769     if( ( begin_hostport = parse_scheme( in, max, &out->scheme ) ) ) {
770         out->type = ABSOLUTE;
771         out->path_type = OPAQUE_PART;
772         begin_hostport++;
773     } else {
774         out->type = RELATIVE;
775         out->path_type = REL_PATH;
776     }
777
778     if( ( ( begin_hostport + 1 ) < max ) && ( in[begin_hostport] == '/' )
779         && ( in[begin_hostport + 1] == '/' ) ) {
780         begin_hostport += 2;
781
782         if( ( begin_path = parse_hostport( &in[begin_hostport],
783                                            max - begin_hostport,
784                                            &out->hostport ) ) >= 0 ) {
785             begin_path += begin_hostport;
786         } else
787             return begin_path;
788
789     } else {
790         memset( &out->hostport, 0, sizeof(out->hostport) );
791         begin_path = begin_hostport;
792     }
793
794     begin_fragment =
795         parse_uric( &in[begin_path], max - begin_path,
796                     &out->pathquery ) + begin_path;
797
798     if( ( out->pathquery.size ) && ( out->pathquery.buff[0] == '/' ) ) {
799         out->path_type = ABS_PATH;
800     }
801
802     if( ( begin_fragment < max ) && ( in[begin_fragment] == '#' ) ) {
803         begin_fragment++;
804         parse_uric( &in[begin_fragment], max - begin_fragment,
805                     &out->fragment );
806     } else {
807         out->fragment.buff = NULL;
808         out->fragment.size = 0;
809     }
810     return HTTP_SUCCESS;
811 }
812
813
814 int parse_uri_and_unescape(char *in, int max, uri_type *out)
815 {
816         int ret = parse_uri(in, max, out);
817
818         if (ret != HTTP_SUCCESS) {
819                 return ret;
820         }
821
822         if (out->pathquery.size > 0) {
823                 remove_escaped_chars((char *)out->pathquery.buff, &out->pathquery.size);
824         }
825         if (out->fragment.size > 0) {
826                 remove_escaped_chars((char *)out->fragment.buff, &out->fragment.size);
827         }
828
829         return HTTP_SUCCESS;
830 }
831