chiark / gitweb /
doc: Document summary values of TOFU_STATS
[gnupg2.git] / dirmngr / ldap-url.c
1 /* The following code comes from the OpenLDAP project.  The references
2    to the COPYRIGHT file below refer to the corresponding file in the
3    OpenLDAP distribution, which is reproduced here in full:
4
5 Copyright 1998-2004 The OpenLDAP Foundation
6 All rights reserved.
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted only as authorized by the OpenLDAP
10 Public License.
11
12 A copy of this license is available in the file LICENSE in the
13 top-level directory of the distribution or, alternatively, at
14 <http://www.OpenLDAP.org/license.html>.
15
16 OpenLDAP is a registered trademark of the OpenLDAP Foundation.
17
18 Individual files and/or contributed packages may be copyright by
19 other parties and subject to additional restrictions.
20
21 This work is derived from the University of Michigan LDAP v3.3
22 distribution.  Information concerning this software is available
23 at <http://www.umich.edu/~dirsvcs/ldap/>.
24
25 This work also contains materials derived from public sources.
26
27 Additional information about OpenLDAP can be obtained at
28 <http://www.openldap.org/>.
29
30 ---
31
32 Portions Copyright 1998-2004 Kurt D. Zeilenga.
33 Portions Copyright 1998-2004 Net Boolean Incorporated.
34 Portions Copyright 2001-2004 IBM Corporation.
35 All rights reserved.
36
37 Redistribution and use in source and binary forms, with or without
38 modification, are permitted only as authorized by the OpenLDAP
39 Public License.
40
41 ---
42
43 Portions Copyright 1999-2003 Howard Y.H. Chu.
44 Portions Copyright 1999-2003 Symas Corporation.
45 Portions Copyright 1998-2003 Hallvard B. Furuseth.
46 All rights reserved.
47
48 Redistribution and use in source and binary forms, with or without
49 modification, are permitted provided that this notice is preserved.
50 The names of the copyright holders may not be used to endorse or
51 promote products derived from this software without their specific
52 prior written permission.  This software is provided `'as is''
53 without express or implied warranty.
54
55 ---
56
57 Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
58 All rights reserved.
59
60 Redistribution and use in source and binary forms are permitted
61 provided that this notice is preserved and that due credit is given
62 to the University of Michigan at Ann Arbor.  The name of the
63 University may not be used to endorse or promote products derived
64 from this software without specific prior written permission.  This
65 software is provided `'as is'' without express or implied warranty.  */
66
67
68 #include <config.h>
69 #include <assert.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <time.h>
74
75 #include <winsock2.h>
76 #include <winldap.h>
77 #include "ldap-url.h"
78 #define LDAP_P(protos)          protos
79 #define LDAP_URL_URLCOLON       "URL:"
80 #define LDAP_URL_URLCOLON_LEN   (sizeof(LDAP_URL_URLCOLON)-1)
81 #define LDAP_URL_PREFIX         "ldap://"
82 #define LDAP_URL_PREFIX_LEN     (sizeof(LDAP_URL_PREFIX)-1)
83 #define LDAPS_URL_PREFIX        "ldaps://"
84 #define LDAPS_URL_PREFIX_LEN    (sizeof(LDAPS_URL_PREFIX)-1)
85 #define LDAPI_URL_PREFIX        "ldapi://"
86 #define LDAPI_URL_PREFIX_LEN    (sizeof(LDAPI_URL_PREFIX)-1)
87 #define LDAP_VFREE(v)           { int _i; for (_i = 0; (v)[_i]; _i++) free((v)[_i]); }
88 #define LDAP_FREE               free
89 #define LDAP_STRDUP             strdup
90 #define LDAP_CALLOC             calloc
91 #define LDAP_MALLOC             malloc
92 #define LDAP_REALLOC            realloc
93 #define ldap_utf8_strchr        strchr
94 #define ldap_utf8_strtok(n,d)   strtok (n,d)
95 #define Debug(a,b,c,d,e)
96 void ldap_pvt_hex_unescape( char *s );
97
98
99 #ifndef LDAP_SCOPE_DEFAULT
100 # define LDAP_SCOPE_DEFAULT -1
101 #endif
102
103
104 \f
105 /* $OpenLDAP: pkg/ldap/libraries/libldap/charray.c,v 1.9.2.2 2003/03/03 17:10:04 kurt Exp $ */
106 /*
107  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
108  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
109  */
110 /* charray.c - routines for dealing with char * arrays */
111
112 int
113 ldap_charray_add(
114     char        ***a,
115     char        *s
116 )
117 {
118         int     n;
119
120         if ( *a == NULL ) {
121                 *a = (char **) LDAP_MALLOC( 2 * sizeof(char *) );
122                 n = 0;
123
124                 if( *a == NULL ) {
125                         return -1;
126                 }
127
128         } else {
129                 char **new;
130
131                 for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
132                         ;       /* NULL */
133                 }
134
135                 new = (char **) LDAP_REALLOC( (char *) *a,
136                     (n + 2) * sizeof(char *) );
137
138                 if( new == NULL ) {
139                         /* caller is required to call ldap_charray_free(*a) */
140                         return -1;
141                 }
142
143                 *a = new;
144         }
145
146         (*a)[n] = LDAP_STRDUP(s);
147
148         if( (*a)[n] == NULL ) {
149                 return 1;
150         }
151
152         (*a)[++n] = NULL;
153
154         return 0;
155 }
156
157 int
158 ldap_charray_merge(
159     char        ***a,
160     char        **s
161 )
162 {
163         int     i, n, nn;
164         char **aa;
165
166         for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
167                 ;       /* NULL */
168         }
169         for ( nn = 0; s[nn] != NULL; nn++ ) {
170                 ;       /* NULL */
171         }
172
173         aa = (char **) LDAP_REALLOC( (char *) *a, (n + nn + 1) * sizeof(char *) );
174
175         if( aa == NULL ) {
176                 return -1;
177         }
178
179         *a = aa;
180
181         for ( i = 0; i < nn; i++ ) {
182                 (*a)[n + i] = LDAP_STRDUP(s[i]);
183
184                 if( (*a)[n + i] == NULL ) {
185                         for( --i ; i >= 0 ; i-- ) {
186                                 LDAP_FREE( (*a)[n + i] );
187                                 (*a)[n + i] = NULL;
188                         }
189                         return -1;
190                 }
191         }
192
193         (*a)[n + nn] = NULL;
194         return 0;
195 }
196
197 void
198 ldap_charray_free( char **a )
199 {
200         char    **p;
201
202         if ( a == NULL ) {
203                 return;
204         }
205
206         for ( p = a; *p != NULL; p++ ) {
207                 if ( *p != NULL ) {
208                         LDAP_FREE( *p );
209                 }
210         }
211
212         LDAP_FREE( (char *) a );
213 }
214
215 int
216 ldap_charray_inlist(
217     char        **a,
218     char        *s
219 )
220 {
221         int     i;
222
223         if( a == NULL ) return 0;
224
225         for ( i=0; a[i] != NULL; i++ ) {
226                 if ( strcasecmp( s, a[i] ) == 0 ) {
227                         return 1;
228                 }
229         }
230
231         return 0;
232 }
233
234 char **
235 ldap_charray_dup( char **a )
236 {
237         int     i;
238         char    **new;
239
240         for ( i = 0; a[i] != NULL; i++ )
241                 ;       /* NULL */
242
243         new = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
244
245         if( new == NULL ) {
246                 return NULL;
247         }
248
249         for ( i = 0; a[i] != NULL; i++ ) {
250                 new[i] = LDAP_STRDUP( a[i] );
251
252                 if( new[i] == NULL ) {
253                         for( --i ; i >= 0 ; i-- ) {
254                                 LDAP_FREE( new[i] );
255                         }
256                         LDAP_FREE( new );
257                         return NULL;
258                 }
259         }
260         new[i] = NULL;
261
262         return( new );
263 }
264
265 char **
266 ldap_str2charray( const char *str_in, const char *brkstr )
267 {
268         char    **res;
269         char    *str, *s;
270         int     i;
271
272         /* protect the input string from strtok */
273         str = LDAP_STRDUP( str_in );
274         if( str == NULL ) {
275                 return NULL;
276         }
277
278         i = 1;
279         for ( s = str; *s; s++ ) {
280                 if ( ldap_utf8_strchr( brkstr, *s ) != NULL ) {
281                         i++;
282                 }
283         }
284
285         res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
286
287         if( res == NULL ) {
288                 LDAP_FREE( str );
289                 return NULL;
290         }
291
292         i = 0;
293
294         for ( s = ldap_utf8_strtok( str, brkstr);
295                 s != NULL;
296                 s = ldap_utf8_strtok( NULL, brkstr) )
297         {
298                 res[i] = LDAP_STRDUP( s );
299
300                 if(res[i] == NULL) {
301                         for( --i ; i >= 0 ; i-- ) {
302                                 LDAP_FREE( res[i] );
303                         }
304                         LDAP_FREE( res );
305                         LDAP_FREE( str );
306                         return NULL;
307                 }
308
309                 i++;
310         }
311
312         res[i] = NULL;
313
314         LDAP_FREE( str );
315         return( res );
316 }
317
318 char * ldap_charray2str( char **a, const char *sep )
319 {
320         char *s, **v, *p;
321         int len;
322         int slen;
323
324         if( sep == NULL ) sep = " ";
325
326         slen = strlen( sep );
327         len = 0;
328
329         for ( v = a; *v != NULL; v++ ) {
330                 len += strlen( *v ) + slen;
331         }
332
333         if ( len == 0 ) {
334                 return NULL;
335         }
336
337         /* trim extra sep len */
338         len -= slen;
339
340         s = LDAP_MALLOC ( len + 1 );
341
342         if ( s == NULL ) {
343                 return NULL;
344         }
345
346         p = s;
347         for ( v = a; *v != NULL; v++ ) {
348                 if ( v != a ) {
349                         strncpy( p, sep, slen );
350                         p += slen;
351                 }
352
353                 len = strlen( *v );
354                 strncpy( p, *v, len );
355                 p += len;
356         }
357
358         *p = '\0';
359         return s;
360 }
361
362
363 \f
364 /* $OpenLDAP: pkg/ldap/libraries/libldap/url.c,v 1.64.2.5 2003/03/03 17:10:05 kurt Exp $ */
365 /*
366  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
367  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
368  */
369 /*  Portions
370  *  Copyright (c) 1996 Regents of the University of Michigan.
371  *  All rights reserved.
372  *
373  *  LIBLDAP url.c -- LDAP URL (RFC 2255) related routines
374  *
375  *  LDAP URLs look like this:
376  *    ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
377  *
378  *  where:
379  *   attributes is a comma separated list
380  *   scope is one of these three strings:  base one sub (default=base)
381  *   filter is an string-represented filter as in RFC 2254
382  *
383  *  e.g.,  ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
384  *
385  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
386  */
387
388 /* local functions */
389 static const char* skip_url_prefix LDAP_P((
390         const char *url,
391         int *enclosedp,
392         const char **scheme ));
393
394 int
395 ldap_is_ldap_url( LDAP_CONST char *url )
396 {
397         int     enclosed;
398         const char * scheme;
399
400         if( url == NULL ) {
401                 return 0;
402         }
403
404         if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
405                 return 0;
406         }
407
408         return 1;
409 }
410
411
412 static const char*
413 skip_url_prefix(
414         const char *url,
415         int *enclosedp,
416         const char **scheme )
417 {
418         /*
419          * return non-zero if this looks like a LDAP URL; zero if not
420          * if non-zero returned, *urlp will be moved past "ldap://" part of URL
421          */
422         const char *p;
423
424         if ( url == NULL ) {
425                 return( NULL );
426         }
427
428         p = url;
429
430         /* skip leading '<' (if any) */
431         if ( *p == '<' ) {
432                 *enclosedp = 1;
433                 ++p;
434         } else {
435                 *enclosedp = 0;
436         }
437
438         /* skip leading "URL:" (if any) */
439         if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
440                 p += LDAP_URL_URLCOLON_LEN;
441         }
442
443         /* check for "ldap://" prefix */
444         if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
445                 /* skip over "ldap://" prefix and return success */
446                 p += LDAP_URL_PREFIX_LEN;
447                 *scheme = "ldap";
448                 return( p );
449         }
450
451         /* check for "ldaps://" prefix */
452         if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
453                 /* skip over "ldaps://" prefix and return success */
454                 p += LDAPS_URL_PREFIX_LEN;
455                 *scheme = "ldaps";
456                 return( p );
457         }
458
459         /* check for "ldapi://" prefix */
460         if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
461                 /* skip over "ldapi://" prefix and return success */
462                 p += LDAPI_URL_PREFIX_LEN;
463                 *scheme = "ldapi";
464                 return( p );
465         }
466
467 #ifdef LDAP_CONNECTIONLESS
468         /* check for "cldap://" prefix */
469         if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
470                 /* skip over "cldap://" prefix and return success */
471                 p += LDAPC_URL_PREFIX_LEN;
472                 *scheme = "cldap";
473                 return( p );
474         }
475 #endif
476
477         return( NULL );
478 }
479
480
481 static int str2scope( const char *p )
482 {
483         if ( strcasecmp( p, "one" ) == 0 ) {
484                 return LDAP_SCOPE_ONELEVEL;
485
486         } else if ( strcasecmp( p, "onetree" ) == 0 ) {
487                 return LDAP_SCOPE_ONELEVEL;
488
489         } else if ( strcasecmp( p, "base" ) == 0 ) {
490                 return LDAP_SCOPE_BASE;
491
492         } else if ( strcasecmp( p, "sub" ) == 0 ) {
493                 return LDAP_SCOPE_SUBTREE;
494
495         } else if ( strcasecmp( p, "subtree" ) == 0 ) {
496                 return LDAP_SCOPE_SUBTREE;
497         }
498
499         return( -1 );
500 }
501
502
503 int
504 ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
505 {
506 /*
507  *  Pick apart the pieces of an LDAP URL.
508  */
509
510         LDAPURLDesc     *ludp;
511         char    *p, *q, *r;
512         int             i, enclosed;
513         const char *scheme = NULL;
514         const char *url_tmp;
515         char *url;
516
517         if( url_in == NULL || ludpp == NULL ) {
518                 return LDAP_URL_ERR_PARAM;
519         }
520
521 #ifndef LDAP_INT_IN_KERNEL
522         /* Global options may not be created yet
523          * We can't test if the global options are initialized
524          * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
525          * the options and cause infinite recursion
526          */
527 #ifdef NEW_LOGGING
528         LDAP_LOG ( OPERATION, ENTRY, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
529 #else
530         Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
531 #endif
532 #endif
533
534         *ludpp = NULL;  /* pessimistic */
535
536         url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
537
538         if ( url_tmp == NULL ) {
539                 return LDAP_URL_ERR_BADSCHEME;
540         }
541
542         assert( scheme );
543
544         /* make working copy of the remainder of the URL */
545         url = LDAP_STRDUP( url_tmp );
546         if ( url == NULL ) {
547                 return LDAP_URL_ERR_MEM;
548         }
549
550         if ( enclosed ) {
551                 p = &url[strlen(url)-1];
552
553                 if( *p != '>' ) {
554                         LDAP_FREE( url );
555                         return LDAP_URL_ERR_BADENCLOSURE;
556                 }
557
558                 *p = '\0';
559         }
560
561         /* allocate return struct */
562         ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
563
564         if ( ludp == NULL ) {
565                 LDAP_FREE( url );
566                 return LDAP_URL_ERR_MEM;
567         }
568
569         ludp->lud_next = NULL;
570         ludp->lud_host = NULL;
571         ludp->lud_port = 0;
572         ludp->lud_dn = NULL;
573         ludp->lud_attrs = NULL;
574         ludp->lud_filter = NULL;
575         ludp->lud_scope = LDAP_SCOPE_DEFAULT;
576         ludp->lud_filter = NULL;
577         ludp->lud_exts = NULL;
578
579         ludp->lud_scheme = LDAP_STRDUP( scheme );
580
581         if ( ludp->lud_scheme == NULL ) {
582                 LDAP_FREE( url );
583                 ldap_free_urldesc( ludp );
584                 return LDAP_URL_ERR_MEM;
585         }
586
587         /* scan forward for '/' that marks end of hostport and begin. of dn */
588         p = strchr( url, '/' );
589
590         if( p != NULL ) {
591                 /* terminate hostport; point to start of dn */
592                 *p++ = '\0';
593         }
594
595         /* IPv6 syntax with [ip address]:port */
596         if ( *url == '[' ) {
597                 r = strchr( url, ']' );
598                 if ( r == NULL ) {
599                         LDAP_FREE( url );
600                         ldap_free_urldesc( ludp );
601                         return LDAP_URL_ERR_BADURL;
602                 }
603                 *r++ = '\0';
604                 q = strchr( r, ':' );
605         } else {
606                 q = strchr( url, ':' );
607         }
608
609         if ( q != NULL ) {
610                 *q++ = '\0';
611                 ldap_pvt_hex_unescape( q );
612
613                 if( *q == '\0' ) {
614                         LDAP_FREE( url );
615                         ldap_free_urldesc( ludp );
616                         return LDAP_URL_ERR_BADURL;
617                 }
618
619                 ludp->lud_port = atoi( q );
620         }
621
622         ldap_pvt_hex_unescape( url );
623
624         /* If [ip address]:port syntax, url is [ip and we skip the [ */
625         ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) );
626
627         if( ludp->lud_host == NULL ) {
628                 LDAP_FREE( url );
629                 ldap_free_urldesc( ludp );
630                 return LDAP_URL_ERR_MEM;
631         }
632
633         /*
634          * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
635          *
636          * On early Novell releases, search references/referrals were returned
637          * in this format, i.e., the dn was kind of in the scope position,
638          * but the required slash is missing. The whole thing is illegal syntax,
639          * but we need to account for it. Fortunately it can't be confused with
640          * anything real.
641          */
642         if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
643                 q++;
644                 /* ? immediately followed by question */
645                 if( *q == '?') {
646                         q++;
647                         if( *q != '\0' ) {
648                                 /* parse dn part */
649                                 ldap_pvt_hex_unescape( q );
650                                 ludp->lud_dn = LDAP_STRDUP( q );
651                         } else {
652                                 ludp->lud_dn = LDAP_STRDUP( "" );
653                         }
654
655                         if( ludp->lud_dn == NULL ) {
656                                 LDAP_FREE( url );
657                                 ldap_free_urldesc( ludp );
658                                 return LDAP_URL_ERR_MEM;
659                         }
660                 }
661         }
662
663         if( p == NULL ) {
664                 LDAP_FREE( url );
665                 *ludpp = ludp;
666                 return LDAP_URL_SUCCESS;
667         }
668
669         /* scan forward for '?' that may marks end of dn */
670         q = strchr( p, '?' );
671
672         if( q != NULL ) {
673                 /* terminate dn part */
674                 *q++ = '\0';
675         }
676
677         if( *p != '\0' ) {
678                 /* parse dn part */
679                 ldap_pvt_hex_unescape( p );
680                 ludp->lud_dn = LDAP_STRDUP( p );
681         } else {
682                 ludp->lud_dn = LDAP_STRDUP( "" );
683         }
684
685         if( ludp->lud_dn == NULL ) {
686                 LDAP_FREE( url );
687                 ldap_free_urldesc( ludp );
688                 return LDAP_URL_ERR_MEM;
689         }
690
691         if( q == NULL ) {
692                 /* no more */
693                 LDAP_FREE( url );
694                 *ludpp = ludp;
695                 return LDAP_URL_SUCCESS;
696         }
697
698         /* scan forward for '?' that may marks end of attributes */
699         p = q;
700         q = strchr( p, '?' );
701
702         if( q != NULL ) {
703                 /* terminate attributes part */
704                 *q++ = '\0';
705         }
706
707         if( *p != '\0' ) {
708                 /* parse attributes */
709                 ldap_pvt_hex_unescape( p );
710                 ludp->lud_attrs = ldap_str2charray( p, "," );
711
712                 if( ludp->lud_attrs == NULL ) {
713                         LDAP_FREE( url );
714                         ldap_free_urldesc( ludp );
715                         return LDAP_URL_ERR_BADATTRS;
716                 }
717         }
718
719         if ( q == NULL ) {
720                 /* no more */
721                 LDAP_FREE( url );
722                 *ludpp = ludp;
723                 return LDAP_URL_SUCCESS;
724         }
725
726         /* scan forward for '?' that may marks end of scope */
727         p = q;
728         q = strchr( p, '?' );
729
730         if( q != NULL ) {
731                 /* terminate the scope part */
732                 *q++ = '\0';
733         }
734
735         if( *p != '\0' ) {
736                 /* parse the scope */
737                 ldap_pvt_hex_unescape( p );
738                 ludp->lud_scope = str2scope( p );
739
740                 if( ludp->lud_scope == -1 ) {
741                         LDAP_FREE( url );
742                         ldap_free_urldesc( ludp );
743                         return LDAP_URL_ERR_BADSCOPE;
744                 }
745         }
746
747         if ( q == NULL ) {
748                 /* no more */
749                 LDAP_FREE( url );
750                 *ludpp = ludp;
751                 return LDAP_URL_SUCCESS;
752         }
753
754         /* scan forward for '?' that may marks end of filter */
755         p = q;
756         q = strchr( p, '?' );
757
758         if( q != NULL ) {
759                 /* terminate the filter part */
760                 *q++ = '\0';
761         }
762
763         if( *p != '\0' ) {
764                 /* parse the filter */
765                 ldap_pvt_hex_unescape( p );
766
767                 if( ! *p ) {
768                         /* missing filter */
769                         LDAP_FREE( url );
770                         ldap_free_urldesc( ludp );
771                         return LDAP_URL_ERR_BADFILTER;
772                 }
773
774                 LDAP_FREE( ludp->lud_filter );
775                 ludp->lud_filter = LDAP_STRDUP( p );
776
777                 if( ludp->lud_filter == NULL ) {
778                         LDAP_FREE( url );
779                         ldap_free_urldesc( ludp );
780                         return LDAP_URL_ERR_MEM;
781                 }
782         }
783
784         if ( q == NULL ) {
785                 /* no more */
786                 LDAP_FREE( url );
787                 *ludpp = ludp;
788                 return LDAP_URL_SUCCESS;
789         }
790
791         /* scan forward for '?' that may marks end of extensions */
792         p = q;
793         q = strchr( p, '?' );
794
795         if( q != NULL ) {
796                 /* extra '?' */
797                 LDAP_FREE( url );
798                 ldap_free_urldesc( ludp );
799                 return LDAP_URL_ERR_BADURL;
800         }
801
802         /* parse the extensions */
803         ludp->lud_exts = ldap_str2charray( p, "," );
804
805         if( ludp->lud_exts == NULL ) {
806                 LDAP_FREE( url );
807                 ldap_free_urldesc( ludp );
808                 return LDAP_URL_ERR_BADEXTS;
809         }
810
811         for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
812                 ldap_pvt_hex_unescape( ludp->lud_exts[i] );
813
814                 if( *ludp->lud_exts[i] == '!' ) {
815                         /* count the number of critical extensions */
816                         ludp->lud_crit_exts++;
817                 }
818         }
819
820         if( i == 0 ) {
821                 /* must have 1 or more */
822                 LDAP_FREE( url );
823                 ldap_free_urldesc( ludp );
824                 return LDAP_URL_ERR_BADEXTS;
825         }
826
827         /* no more */
828         *ludpp = ludp;
829         LDAP_FREE( url );
830         return LDAP_URL_SUCCESS;
831 }
832
833 int
834 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
835 {
836         int rc = ldap_url_parse_ext( url_in, ludpp );
837
838         if( rc != LDAP_URL_SUCCESS ) {
839                 return rc;
840         }
841
842         if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) {
843                 (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
844         }
845
846         if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
847                 LDAP_FREE( (*ludpp)->lud_host );
848                 (*ludpp)->lud_host = NULL;
849         }
850
851         if ((*ludpp)->lud_port == 0) {
852                 if( strcmp((*ludpp)->lud_scheme, "ldap") == 0 ) {
853                         (*ludpp)->lud_port = LDAP_PORT;
854 #ifdef LDAP_CONNECTIONLESS
855                 } else if( strcmp((*ludpp)->lud_scheme, "cldap") == 0 ) {
856                         (*ludpp)->lud_port = LDAP_PORT;
857 #endif
858                 } else if( strcmp((*ludpp)->lud_scheme, "ldaps") == 0 ) {
859                         (*ludpp)->lud_port = LDAPS_PORT;
860                 }
861         }
862
863         return rc;
864 }
865
866
867 void
868 ldap_free_urldesc( LDAPURLDesc *ludp )
869 {
870         if ( ludp == NULL ) {
871                 return;
872         }
873
874         if ( ludp->lud_scheme != NULL ) {
875                 LDAP_FREE( ludp->lud_scheme );
876         }
877
878         if ( ludp->lud_host != NULL ) {
879                 LDAP_FREE( ludp->lud_host );
880         }
881
882         if ( ludp->lud_dn != NULL ) {
883                 LDAP_FREE( ludp->lud_dn );
884         }
885
886         if ( ludp->lud_filter != NULL ) {
887                 LDAP_FREE( ludp->lud_filter);
888         }
889
890         if ( ludp->lud_attrs != NULL ) {
891                 LDAP_VFREE( ludp->lud_attrs );
892         }
893
894         if ( ludp->lud_exts != NULL ) {
895                 LDAP_VFREE( ludp->lud_exts );
896         }
897
898         LDAP_FREE( ludp );
899 }
900
901
902 static int
903 ldap_int_unhex( int c )
904 {
905         return( c >= '0' && c <= '9' ? c - '0'
906             : c >= 'A' && c <= 'F' ? c - 'A' + 10
907             : c - 'a' + 10 );
908 }
909
910 void
911 ldap_pvt_hex_unescape( char *s )
912 {
913         /*
914          * Remove URL hex escapes from s... done in place.  The basic concept for
915          * this routine is borrowed from the WWW library HTUnEscape() routine.
916          */
917         char    *p;
918
919         for ( p = s; *s != '\0'; ++s ) {
920                 if ( *s == '%' ) {
921                         if ( *++s == '\0' ) {
922                                 break;
923                         }
924                         *p = ldap_int_unhex( *s ) << 4;
925                         if ( *++s == '\0' ) {
926                                 break;
927                         }
928                         *p++ += ldap_int_unhex( *s );
929                 } else {
930                         *p++ = *s;
931                 }
932         }
933
934         *p = '\0';
935 }