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