bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / srclib / apr-util / ldap / apr_ldap_url.c
1 /* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /* Portions Copyright 1998-2002 The OpenLDAP Foundation
18  * All rights reserved.
19  * 
20  * Redistribution and use in source and binary forms, with or without
21  * modification, are permitted only as authorized by the OpenLDAP
22  * Public License.  A copy of this license is available at
23  * http://www.OpenLDAP.org/license.html or in file LICENSE in the
24  * top-level directory of the distribution.
25  * 
26  * OpenLDAP is a registered trademark of the OpenLDAP Foundation.
27  * 
28  * Individual files and/or contributed packages may be copyright by
29  * other parties and subject to additional restrictions.
30  * 
31  * This work is derived from the University of Michigan LDAP v3.3
32  * distribution.  Information concerning this software is available
33  * at: http://www.umich.edu/~dirsvcs/ldap/
34  * 
35  * This work also contains materials derived from public sources.
36  * 
37  * Additional information about OpenLDAP can be obtained at:
38  *     http://www.openldap.org/
39  */
40
41 /* 
42  * Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
43  * All rights reserved.
44  * 
45  * Redistribution and use in source and binary forms are permitted
46  * provided that this notice is preserved and that due credit is given
47  * to the University of Michigan at Ann Arbor. The name of the University
48  * may not be used to endorse or promote products derived from this
49  * software without specific prior written permission. This software
50  * is provided ``as is'' without express or implied warranty.
51  */
52
53 /*  apr_ldap_url.c -- LDAP URL (RFC 2255) related routines
54  *
55  *  Win32 and perhaps other non-OpenLDAP based ldap libraries may be
56  *  missing ldap_url_* APIs.  We focus here on the one significant
57  *  aspect, which is parsing.  We have [for the time being] omitted
58  *  the ldap_url_search APIs.
59  *
60  *  LDAP URLs look like this:
61  *    ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
62  *
63  *  where:
64  *   attributes is a comma separated list
65  *   scope is one of these three strings:  base one sub (default=base)
66  *   filter is an string-represented filter as in RFC 2254
67  *
68  *  e.g.,  ldap://host:port/dc=com?o,cn?base?o=openldap?extension
69  *
70  *  Tolerates URLs that look like: <ldapurl> and <URL:ldapurl>
71  */
72
73 #include "apr_ldap.h"
74
75 #if APR_HAS_LDAP
76
77 #if !APR_HAS_LDAP_URL_PARSE
78
79 #include "apr_general.h"
80 #include "apr_strings.h"
81
82 #ifndef LDAPS_PORT
83 #define LDAPS_PORT              636  /* ldaps:/// default LDAP over TLS port */
84 #endif
85
86 #define LDAP_URL_PREFIX         "ldap://"
87 #define LDAP_URL_PREFIX_LEN     (sizeof(LDAP_URL_PREFIX)-1)
88 #define LDAPS_URL_PREFIX        "ldaps://"
89 #define LDAPS_URL_PREFIX_LEN    (sizeof(LDAPS_URL_PREFIX)-1)
90 #define LDAPI_URL_PREFIX        "ldapi://"
91 #define LDAPI_URL_PREFIX_LEN    (sizeof(LDAPI_URL_PREFIX)-1)
92 #define LDAP_URL_URLCOLON       "URL:"
93 #define LDAP_URL_URLCOLON_LEN   (sizeof(LDAP_URL_URLCOLON)-1)
94
95 #define LDAP_STRDUP(x) strdup(x)
96 #define LDAP_CALLOC(n, s) calloc(n, s)
97 #define LDAP_MALLOC(n) malloc(n)
98 #define LDAP_REALLOC(x, n) realloc(x, n)
99 #define LDAP_FREE(x) free(x)
100 #define LDAP_VFREE(a) ldap_charray_free(a)
101
102 #define ldap_utf8_strchr(x, s) strchr(x, *s)
103 #define ldap_utf8_strtok(x, s, l) apr_strtok(x, s, l)
104
105 /* local functions */
106 static const char* skip_url_prefix(const char *url, int *enclosedp,
107                                    const char **scheme);
108
109 static void ldap_pvt_hex_unescape(char *s);
110
111 static int ldap_pvt_unhex(int c);
112
113 static void ldap_charray_free(char **a);
114
115 static char **ldap_str2charray(const char *str, const char *brkstr);
116
117 APU_DECLARE(int) apr_ldap_is_ldap_url(const char *url)
118 {
119     int enclosed;
120     const char * scheme;
121
122     if( url == NULL ) {
123         return 0;
124     }
125
126     if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
127         return 0;
128     }
129
130     return 1;
131 }
132
133 APU_DECLARE(int) apr_ldap_is_ldaps_url(const char *url)
134 {
135     int enclosed;
136     const char * scheme;
137
138     if( url == NULL ) {
139         return 0;
140     }
141
142     if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
143         return 0;
144     }
145
146     return strcmp(scheme, "ldaps") == 0;
147 }
148
149 APU_DECLARE(int) apr_ldap_is_ldapi_url(const char *url)
150 {
151     int enclosed;
152     const char * scheme;
153
154     if( url == NULL ) {
155         return 0;
156     }
157
158     if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
159         return 0;
160     }
161
162     return strcmp(scheme, "ldapi") == 0;
163 }
164
165 static const char *skip_url_prefix(const char *url, int *enclosedp,
166                                    const char **scheme)
167 {
168     /*
169      * return non-zero if this looks like a LDAP URL; zero if not
170      * if non-zero returned, *urlp will be moved past "ldap://" part of URL
171      */
172     const char *p;
173
174     if ( url == NULL ) {
175         return( NULL );
176     }
177
178     p = url;
179
180     /* skip leading '<' (if any) */
181     if ( *p == '<' ) {
182         *enclosedp = 1;
183         ++p;
184     } else {
185         *enclosedp = 0;
186     }
187
188     /* skip leading "URL:" (if any) */
189     if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
190         p += LDAP_URL_URLCOLON_LEN;
191     }
192
193     /* check for "ldap://" prefix */
194     if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
195         /* skip over "ldap://" prefix and return success */
196         p += LDAP_URL_PREFIX_LEN;
197         *scheme = "ldap";
198         return( p );
199     }
200
201     /* check for "ldaps://" prefix */
202     if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
203         /* skip over "ldaps://" prefix and return success */
204         p += LDAPS_URL_PREFIX_LEN;
205         *scheme = "ldaps";
206         return( p );
207     }
208
209     /* check for "ldapi://" prefix */
210     if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
211         /* skip over "ldapi://" prefix and return success */
212         p += LDAPI_URL_PREFIX_LEN;
213         *scheme = "ldapi";
214         return( p );
215     }
216
217     return( NULL );
218 }
219
220
221 static int str2scope(const char *p)
222 {
223     if ( strcasecmp( p, "one" ) == 0 ) {
224         return LDAP_SCOPE_ONELEVEL;
225
226     } else if ( strcasecmp( p, "onetree" ) == 0 ) {
227         return LDAP_SCOPE_ONELEVEL;
228
229     } else if ( strcasecmp( p, "base" ) == 0 ) {
230         return LDAP_SCOPE_BASE;
231
232     } else if ( strcasecmp( p, "sub" ) == 0 ) {
233         return LDAP_SCOPE_SUBTREE;
234
235     } else if ( strcasecmp( p, "subtree" ) == 0 ) {
236         return LDAP_SCOPE_SUBTREE;
237     }
238
239     return( -1 );
240 }
241
242
243 static int ldap_url_parse_ext(const char *url_in, 
244                               apr_ldap_url_desc_t **ludpp)
245 {
246 /*
247  *  Pick apart the pieces of an LDAP URL.
248  */
249     apr_ldap_url_desc_t *ludp;
250     char        *p, *q, *r;
251     int         i, enclosed;
252     const char  *scheme = NULL;
253     const char  *url_tmp;
254     char        *url;
255
256     if( url_in == NULL || ludpp == NULL ) {
257         return LDAP_URL_ERR_PARAM;
258     }
259
260     *ludpp = NULL;  /* pessimistic */
261
262     url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
263
264     if ( url_tmp == NULL ) {
265         return LDAP_URL_ERR_BADSCHEME;
266     }
267
268     /* make working copy of the remainder of the URL */
269     url = LDAP_STRDUP( url_tmp );
270     if ( url == NULL ) {
271         return LDAP_URL_ERR_MEM;
272     }
273
274     if ( enclosed ) {
275         p = &url[strlen(url)-1];
276
277         if( *p != '>' ) {
278             LDAP_FREE( url );
279             return LDAP_URL_ERR_BADENCLOSURE;
280         }
281
282         *p = '\0';
283     }
284
285     /* allocate return struct */
286     ludp = (apr_ldap_url_desc_t *)LDAP_CALLOC( 1, sizeof( apr_ldap_url_desc_t ));
287
288     if ( ludp == NULL ) {
289         LDAP_FREE( url );
290         return LDAP_URL_ERR_MEM;
291     }
292
293     ludp->lud_next = NULL;
294     ludp->lud_host = NULL;
295     ludp->lud_port = LDAP_PORT;
296     ludp->lud_dn = NULL;
297     ludp->lud_attrs = NULL;
298     ludp->lud_filter = NULL;
299     ludp->lud_scope = -1;
300     ludp->lud_filter = NULL;
301     ludp->lud_exts = NULL;
302
303     ludp->lud_scheme = LDAP_STRDUP( scheme );
304
305     if ( ludp->lud_scheme == NULL ) {
306         LDAP_FREE( url );
307         apr_ldap_free_urldesc( ludp );
308         return LDAP_URL_ERR_MEM;
309     }
310
311     if( strcasecmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
312         ludp->lud_port = LDAPS_PORT;
313     }
314
315     /* scan forward for '/' that marks end of hostport and begin. of dn */
316     p = strchr( url, '/' );
317
318     if( p != NULL ) {
319         /* terminate hostport; point to start of dn */
320         *p++ = '\0';
321     }
322
323     /* IPv6 syntax with [ip address]:port */
324     if ( *url == '[' ) {
325         r = strchr( url, ']' );
326         if ( r == NULL ) {
327             LDAP_FREE( url );
328             apr_ldap_free_urldesc( ludp );
329             return LDAP_URL_ERR_BADURL;
330         }
331         *r++ = '\0';
332         q = strchr( r, ':' );
333     } else {
334         q = strchr( url, ':' );
335     }
336
337     if ( q != NULL ) {
338         *q++ = '\0';
339         ldap_pvt_hex_unescape( q );
340
341         if( *q == '\0' ) {
342             LDAP_FREE( url );
343             apr_ldap_free_urldesc( ludp );
344             return LDAP_URL_ERR_BADURL;
345         }
346
347         ludp->lud_port = atoi( q );
348     }
349
350     ldap_pvt_hex_unescape( url );
351
352     /* If [ip address]:port syntax, url is [ip and we skip the [ */
353     ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) );
354
355     if( ludp->lud_host == NULL ) {
356         LDAP_FREE( url );
357         apr_ldap_free_urldesc( ludp );
358         return LDAP_URL_ERR_MEM;
359     }
360
361     /*
362      * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
363      *
364      * On early Novell releases, search references/referrals were returned
365      * in this format, i.e., the dn was kind of in the scope position,
366      * but the required slash is missing. The whole thing is illegal syntax,
367      * but we need to account for it. Fortunately it can't be confused with
368      * anything real.
369      */
370     if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
371         q++;
372         /* ? immediately followed by question */
373         if( *q == '?') {
374             q++;
375             if( *q != '\0' ) {
376                 /* parse dn part */
377                 ldap_pvt_hex_unescape( q );
378                 ludp->lud_dn = LDAP_STRDUP( q );
379             } else {
380                 ludp->lud_dn = LDAP_STRDUP( "" );
381             }
382
383             if( ludp->lud_dn == NULL ) {
384                 LDAP_FREE( url );
385                 apr_ldap_free_urldesc( ludp );
386                 return LDAP_URL_ERR_MEM;
387             }
388         }
389     }
390
391     if( p == NULL ) {
392         LDAP_FREE( url );
393         *ludpp = ludp;
394         return LDAP_URL_SUCCESS;
395     }
396
397     /* scan forward for '?' that may marks end of dn */
398     q = strchr( p, '?' );
399
400     if( q != NULL ) {
401         /* terminate dn part */
402         *q++ = '\0';
403     }
404
405     if( *p != '\0' ) {
406         /* parse dn part */
407         ldap_pvt_hex_unescape( p );
408         ludp->lud_dn = LDAP_STRDUP( p );
409     } else {
410         ludp->lud_dn = LDAP_STRDUP( "" );
411     }
412
413     if( ludp->lud_dn == NULL ) {
414         LDAP_FREE( url );
415         apr_ldap_free_urldesc( ludp );
416         return LDAP_URL_ERR_MEM;
417     }
418
419     if( q == NULL ) {
420         /* no more */
421         LDAP_FREE( url );
422         *ludpp = ludp;
423         return LDAP_URL_SUCCESS;
424     }
425
426     /* scan forward for '?' that may marks end of attributes */
427     p = q;
428     q = strchr( p, '?' );
429
430     if( q != NULL ) {
431         /* terminate attributes part */
432         *q++ = '\0';
433     }
434
435     if( *p != '\0' ) {
436         /* parse attributes */
437         ldap_pvt_hex_unescape( p );
438         ludp->lud_attrs = ldap_str2charray( p, "," );
439
440         if( ludp->lud_attrs == NULL ) {
441             LDAP_FREE( url );
442             apr_ldap_free_urldesc( ludp );
443             return LDAP_URL_ERR_BADATTRS;
444         }
445     }
446
447     if ( q == NULL ) {
448         /* no more */
449         LDAP_FREE( url );
450         *ludpp = ludp;
451         return LDAP_URL_SUCCESS;
452     }
453
454     /* scan forward for '?' that may marks end of scope */
455     p = q;
456     q = strchr( p, '?' );
457
458     if( q != NULL ) {
459         /* terminate the scope part */
460         *q++ = '\0';
461     }
462
463     if( *p != '\0' ) {
464         /* parse the scope */
465         ldap_pvt_hex_unescape( p );
466         ludp->lud_scope = str2scope( p );
467
468         if( ludp->lud_scope == -1 ) {
469             LDAP_FREE( url );
470             apr_ldap_free_urldesc( ludp );
471             return LDAP_URL_ERR_BADSCOPE;
472         }
473     }
474
475     if ( q == NULL ) {
476         /* no more */
477         LDAP_FREE( url );
478         *ludpp = ludp;
479         return LDAP_URL_SUCCESS;
480     }
481
482     /* scan forward for '?' that may marks end of filter */
483     p = q;
484     q = strchr( p, '?' );
485
486     if( q != NULL ) {
487         /* terminate the filter part */
488         *q++ = '\0';
489     }
490
491     if( *p != '\0' ) {
492         /* parse the filter */
493         ldap_pvt_hex_unescape( p );
494
495         if( ! *p ) {
496             /* missing filter */
497             LDAP_FREE( url );
498             apr_ldap_free_urldesc( ludp );
499             return LDAP_URL_ERR_BADFILTER;
500         }
501
502         LDAP_FREE( ludp->lud_filter );
503         ludp->lud_filter = LDAP_STRDUP( p );
504
505         if( ludp->lud_filter == NULL ) {
506             LDAP_FREE( url );
507             apr_ldap_free_urldesc( ludp );
508             return LDAP_URL_ERR_MEM;
509         }
510     }
511
512     if ( q == NULL ) {
513         /* no more */
514         LDAP_FREE( url );
515         *ludpp = ludp;
516         return LDAP_URL_SUCCESS;
517     }
518
519     /* scan forward for '?' that may marks end of extensions */
520     p = q;
521     q = strchr( p, '?' );
522
523     if( q != NULL ) {
524         /* extra '?' */
525         LDAP_FREE( url );
526         apr_ldap_free_urldesc( ludp );
527         return LDAP_URL_ERR_BADURL;
528     }
529
530     /* parse the extensions */
531     ludp->lud_exts = ldap_str2charray( p, "," );
532
533     if( ludp->lud_exts == NULL ) {
534         LDAP_FREE( url );
535         apr_ldap_free_urldesc( ludp );
536         return LDAP_URL_ERR_BADEXTS;
537     }
538
539     for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
540         ldap_pvt_hex_unescape( ludp->lud_exts[i] );
541
542         if( *ludp->lud_exts[i] == '!' ) {
543             /* count the number of critical extensions */
544             ludp->lud_crit_exts++;
545         }
546     }
547
548     if( i == 0 ) {
549         /* must have 1 or more */
550         LDAP_FREE( url );
551         apr_ldap_free_urldesc( ludp );
552         return LDAP_URL_ERR_BADEXTS;
553     }
554
555     /* no more */
556     *ludpp = ludp;
557     LDAP_FREE( url );
558     return LDAP_URL_SUCCESS;
559 }
560
561 APU_DECLARE(int) apr_ldap_url_parse(const char *url_in, 
562                                     apr_ldap_url_desc_t **ludpp)
563 {
564     int rc = ldap_url_parse_ext( url_in, ludpp );
565
566     if( rc != LDAP_URL_SUCCESS ) {
567         return rc;
568     }
569
570     if ((*ludpp)->lud_scope == -1) {
571         (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
572     }
573
574     if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
575         LDAP_FREE( (*ludpp)->lud_host );
576         (*ludpp)->lud_host = NULL;
577     }
578
579     return rc;
580 }
581
582 APU_DECLARE(void) apr_ldap_free_urldesc(apr_ldap_url_desc_t *ludp)
583 {
584     if ( ludp == NULL ) {
585         return;
586     }
587     
588     if ( ludp->lud_scheme != NULL ) {
589         LDAP_FREE( ludp->lud_scheme );
590     }
591
592     if ( ludp->lud_host != NULL ) {
593         LDAP_FREE( ludp->lud_host );
594     }
595
596     if ( ludp->lud_dn != NULL ) {
597         LDAP_FREE( ludp->lud_dn );
598     }
599
600     if ( ludp->lud_filter != NULL ) {
601         LDAP_FREE( ludp->lud_filter);
602     }
603
604     if ( ludp->lud_attrs != NULL ) {
605         LDAP_VFREE( ludp->lud_attrs );
606     }
607
608     if ( ludp->lud_exts != NULL ) {
609         LDAP_VFREE( ludp->lud_exts );
610     }
611
612     LDAP_FREE( ludp );
613 }
614
615
616 static void ldap_pvt_hex_unescape(char *s)
617 {
618     /*
619      * Remove URL hex escapes from s... done in place.  The basic concept for
620      * this routine is borrowed from the WWW library HTUnEscape() routine.
621      */
622     char    *p;
623
624     for ( p = s; *s != '\0'; ++s ) {
625         if ( *s == '%' ) {
626             if ( *++s == '\0' ) {
627                 break;
628             }
629             *p = ldap_pvt_unhex( *s ) << 4;
630             if ( *++s == '\0' ) {
631                 break;
632             }
633             *p++ += ldap_pvt_unhex( *s );
634         } else {
635             *p++ = *s;
636         }
637     }
638
639     *p = '\0';
640 }
641
642
643 static int ldap_pvt_unhex(int c)
644 {
645     return( c >= '0' && c <= '9' ? c - '0'
646         : c >= 'A' && c <= 'F' ? c - 'A' + 10
647         : c - 'a' + 10 );
648 }
649
650
651 static void ldap_charray_free(char **a)
652 {
653     char    **p;
654
655     if ( a == NULL ) {
656         return;
657     }
658
659     for ( p = a; *p != NULL; p++ ) {
660         if ( *p != NULL ) {
661             LDAP_FREE( *p );
662         }
663     }
664
665     LDAP_FREE( (char *) a );
666 }
667
668 static char **ldap_str2charray(const char *str_in, const char *brkstr)
669 {
670     char    **res;
671     char    *str, *s;
672     char    *lasts;
673     int i;
674
675     /* protect the input string from strtok */
676     str = LDAP_STRDUP( str_in );
677     if( str == NULL ) {
678         return NULL;
679     }
680
681     i = 1;
682     for ( s = str; *s; s++ ) {
683         if ( ldap_utf8_strchr( brkstr, s ) != NULL ) {
684             i++;
685         }
686     }
687
688     res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
689
690     if( res == NULL ) {
691         LDAP_FREE( str );
692         return NULL;
693     }
694
695     i = 0;
696
697     for ( s = ldap_utf8_strtok( str, brkstr, &lasts );
698         s != NULL;
699         s = ldap_utf8_strtok( NULL, brkstr, &lasts ) )
700     {
701         res[i] = LDAP_STRDUP( s );
702
703         if(res[i] == NULL) {
704             for( --i ; i >= 0 ; i-- ) {
705                 LDAP_FREE( res[i] );
706             }
707             LDAP_FREE( res );
708             LDAP_FREE( str );
709             return NULL;
710         }
711
712         i++;
713     }
714
715     res[i] = NULL;
716
717     LDAP_FREE( str );
718     return( res );
719 }
720
721 #endif /* !APR_HAS_LDAP_URL_PARSE */
722
723 #endif /* APR_HAS_LDAP */