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